changeset 6:a9b8b0866ece

clear out log jam
author Nenue
date Sun, 21 Feb 2016 08:32:53 -0500
parents 8a9a6637f082
children bdd560f6d36d
files .pkgmeta Equip.lua Fog.lua Lost.lua Spirit.lua Tek.lua Turok.iml Turok.lua Turok.toc Turok/.pkgmeta Turok/Init.lua Turok/Layout/Dialog.lua Turok/Layout/Dialog.xml Turok/Layout/Layout.lua Turok/Layout/Layout.xml Turok/Media.lua Turok/Media/Manifest.bat.txt Turok/Media/border/BG-Solid.blp Turok/Media/border/BG-Solid.png Turok/Media/border/BigBorder-Solid.blp Turok/Media/border/BigBorder-Solid.png Turok/Media/sound/Bell.ogg Turok/Media/sound/Chime.ogg Turok/Media/sound/Electro_-S_Bainbr-7953_hifi.mp3 Turok/Media/sound/Electro_-S_Bainbr-7955_hifi.mp3 Turok/Media/sound/FLASH.mp3 Turok/Media/sound/Heart.ogg Turok/Media/sound/High_Bee-Public_D-135_hifi.mp3 Turok/Media/sound/IM.ogg Turok/Media/sound/Info.ogg Turok/Media/sound/Kachink.ogg Turok/Media/sound/Link.ogg Turok/Media/sound/Low_Beep-Public_D-136_hifi.mp3 Turok/Media/sound/Oringz-881.mp3 Turok/Media/sound/Oringz-940.mp3 Turok/Media/sound/Quack.ogg Turok/Media/sound/SquishFart.ogg Turok/Media/sound/Text1.ogg Turok/Media/sound/Text2.ogg Turok/Media/sound/Xylo.ogg Turok/Media/sound/audiocredits.txt Turok/Media/sound/polish-xrikazen-7425_hifi.mp3 Turok/Media/statusbar/Aluminium.tga Turok/Media/statusbar/Armory.tga Turok/Media/statusbar/Minimalist.tga Turok/Media/statusbar/Otravi.tga Turok/Modules/Combat/Castbar.Init.lua Turok/Modules/Combat/Castbar.lua Turok/Modules/Combat/Castbar.xml Turok/Modules/Combat/Combat.xml Turok/Modules/Combat/CombatLog.lua Turok/Modules/Combat/CombatLog.xml Turok/Modules/Combat/Powerbar.Init.lua Turok/Modules/Combat/Powerbar.lua Turok/Modules/Combat/Powerbar.xml Turok/Modules/Timer/Aura.lua Turok/Modules/Timer/Container.lua Turok/Modules/Timer/Cooldown.lua Turok/Modules/Timer/Editor.lua Turok/Modules/Timer/Icon.lua Turok/Modules/Timer/Import.lua Turok/Modules/Timer/Presets.lua Turok/Modules/Timer/Progressbar.lua Turok/Modules/Timer/Status.lua Turok/Modules/Timer/Timer.Init.lua Turok/Modules/Timer/Timer.lua Turok/Modules/Timer/Timer.xml Turok/Modules/Utilities/Chat.lua Turok/Modules/Utilities/PetBattle.lua Turok/Modules/Utilities/Raid.lua Turok/Modules/Utilities/Raid.xml Turok/Modules/Utilities/Toast.lua Turok/Modules/Utilities/Utilities.xml Turok/Turok.iml Turok/Turok.lua Turok/Turok.toc Turok/Turok.xml Turok/Turok_Config.lua Turok/readme.txt Turok_Config/Turok_Config.toc readme.txt
diffstat 80 files changed, 10073 insertions(+), 1465 deletions(-) [+]
line wrap: on
line diff
--- a/.pkgmeta	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-externals:
-    Libs/LibStub:
-        url: svn://svn.wowace.com/wow/libstub/mainline/trunk
-        tag: latest
-    Libs/CallbackHandler-1.0:
-        url: svn://svn.wowace.com/wow/callbackhandler/mainline/trunk/CallbackHandler-1.0
-        tag: latest
-    Libs/AceAddon-3.0:
-        url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0
-        tag: latest
-
-
-ignore:
-    - Debug.lua
-    - Turok.iml
-    - .idea
-
-enable-nolib-creation: no
\ No newline at end of file
--- a/Equip.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,94 +0,0 @@
--- User: Krakyn
--- Created: 12/4/2015 11:20 PM
--- Equipment, spec, etc.
-local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
-local mod = T:NewModule("Equip", "AceEvent-3.0")
---setprinthandler(function (...) T:debug('Equip', nil, ...) end)
-
-local t18_slots = {
-  [INVSLOT_HEAD] = 124296, -- head
-  [INVSLOT_SHOULDER] = 124307, -- shoulders
-  [INVSLOT_CHEST] = 124284, -- chest
-  [INVSLOT_LEGS] = 124301, -- legs
-  [INVSLOT_HAND] = 124292, -- hands
-}
-
-
-local use_slots = {
-  [INVSLOT_FINGER1] = 1,
-  [INVSLOT_FINGER2] = 2,
-  [INVSLOT_TRINKET1] = 3,
-  [INVSLOT_TRINKET2] = 4,
-
-}
-function mod:OnEnable()
-  self.equipped = {}
-  self:RegisterEvent('COMBAT_RATING_UPDATE', 'UpdateCombatRatings')
-  self:RegisterEvent('PLAYER_EQUIPMENT_CHANGED', 'UpdateEquipment')
-  self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', 'UpdateSpec')
-end
-
-function mod:UpdateEquipment (_, slot, hasItem)
-  -- ratings event will handle the rest
-  if not t18_slots[slot] or use_slots[slot] then
-    return
-  end
-  -- debug
-  local link = GetInventoryItemID('player', slot)
-  print('|cFF00FFFFequipment ', slot, hasItem, link)
-  -- /debug
-
-  if t18_slots[slot] then
-    t18_count = 0
-    for slot, itemID in pairs(t18_slots) do
-      if GetInventoryItemID(slot) == itemID then
-        t18_count = t18_count + 1
-      end
-    end
-    if t18 >= 4 then
-      T.stats.aimedshot = s.gcd
-    else
-      T.stats.aimedshot = 2 * T.stats.castingmod
-    end
-    T.statstext =string.format('%.2f', s.aimedshot)
-  end
-
-  if use_slots[slot] then
-    if hasItem then
-      local itemID = GetInventoryItemID('player', slot)
-      self.equipped[slot] = itemID
-      T:GetModule("Spirit").item_watch[itemID] = {GetInventoryItemCooldown('player', slot) }
-    else
-      if self.equipped[slot] then
-        self.equipped[slot] = nil
-      end
-
-    end
-  end
-
-end
-
-
-function mod:UpdateCombatRatings()
-  local s = T.stats
-  s.haste = UnitSpellHaste('player')
-  s.focusregen = 4 * (1 + s.haste/100)
-  s.castingmod = 1+ s.haste/1000
-  s.gcd = 1.5 * s.castingmod
-  T.statstext = {
-    haste = string.format('%.2f', s.haste),
-    focusregen = string.format('%.2f', s.focusregen),
-    castingmod = string.format('%.2f', s.castingmod),
-  }
-  local t = T.statstext
-
-  print('|cFF00FFFFCOMBAT_RATING_UPDATE:|r', 'haste:', t.haste, 'regen:', t.focusregen)
-end
-
-function mod:UpdateSpec (e, unit)
-  if unit ~= 'player' then
-    return
-  end
-
-  T.stats.focusmax = UnitPowerMax('player')
-end
\ No newline at end of file
--- a/Fog.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,325 +0,0 @@
--- User: Krakyn
--- Created: 12/4/2015 11:13 PM
---[[
---  Core tools, mostly frame handling business. It's an interface script after all.
---  Types:
---  StatusFrame
---    Primary container for status data, carries primary value information by default
---  FIELDS
---    value              = exact primary value represented by frame
---    min                = exact minimum for primary value
---    max                = exact maximum for primary value
---    percent            = value / max
---    alpha              = base frame alpha level, set when a fade animation has completed
---    fadeTo             = when set, the frame will begin fading to this level
---    fadeDuration       = set with fadeTo when the transition needs to be time-adjusted for unpredictable alpha states
---    fadeTime           = set with fadeTo when the transition has an explicit desired timing
---    throttle_rate      = animation frame delay
---
---  METHODS
---    Update()           = Runs the frame's Update script, and calls UpdateText()
---    UpdateText()       = Iterates over frame children and calls Update() if it is a StatusText frame
---
---  StatusText
---    Sub-container for individual text elements, carries string output values, controlled by from Update(). By default
---    these are derived from the parent's primary value data, but is mostly overridden in module code.
---  FIELDS
---    text               = WoW FontString object housing text data
---    format             = a format string for expressing complex data (timer bars, etc.)
---  METHODS
---    SetText()          = shortcut for text:SetText(...)
---    SetJustifyH()      = shortcut for text:SetJustifyH(...)
--- Implements:
--- frame = CreateBar(name, config root)
---   Creates and displays a generic bar frame with progress textures and default updater, based on the variables from
---   the smart config table. Seeded with methods Update() and UpdateText().
---   Update() will refresh the entire frame and tail call UpdateText()
---   UpdateText() will iterate over the contents of frame:GetChildren() and call Update()
---   NEVER CREATE A CHILD FRAME WITHOUT Update() DEFINED
---   NEVER INVOKE METHODS FROM A GetParent() RETURN
---
--- Bar_SetUpdateHandler(frame, function)
---   Takes the desired OnUpdate script and wraps it inside frame throttling logic, then does SetScript
---
--- frame = AddLabel(frame, config root, name)
---   Constructs a text display and adds it to the frame's list of internal text objects. Contains method Update(), which
---   refreshed text.
---
--- HANDLERS
--- Bar_Update([value, min/duration, max/end])
---   Default handler for any non-timed bar (where isTimer = false in config vars. Invoked by frame:Update()
-]]
-local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
-local LSM = LibStub("LibSharedMedia-3.0")
-local TL = 'Fog'
-local print = function(...)
-  _G.print(TL, ...)
-end
---setprinthandler(function (...) T:debug('Fog', nil, ...) end)
-local FADE_OUT_TIME = 1   -- duration per 1-0 alpha transition
-local FADE_IN_TIME = 0.4  -- duration per 0-1 alpha transition
-
-local inset_factor = {
-  ['TOPLEFT']     = {-1, 1},
-  ['TOP']         = {0,  1},
-  ['TOPRIGHT']    = {1,  1},
-  ['RIGHT']       = {1,  0},
-  ['BOTTOMRIGHT'] = {1, -1},
-  ['BOTTOM']      = {0, -1},
-  ['BOTTOMLEFT']  = {-1,-1},
-  ['LEFT']        = {-1, 0}
-}
-
-T.anchor_inverse = { ['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['UP'] = 'DOWN', ['DOWN'] = 'UP' }  -- directional inverse
-T.direction_coord = { ['LEFT'] = {1, 0}, ['RIGHT'] = {-1,0}, ['UP'] = {0, -1}, ['DOWN'] = {0, 1 } } -- directional derivatives
-T.anchor_direction = { ['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['UP'] = 'BOTTOM', ['DOWN'] = 'TOP'}  -- directional anchors
-
-function T:CreateBar(name, config)
-  local parent = type(config.parent) == 'string' and _G[config.parent] or parent
-  if not parent then
-    parent = CreateFrame('Frame', config.parent, UIParent)
-    print('creating dry frame |cFFDDDDDD' .. config.parent .. '|r for |cFFFFFF00' .. name .. '|r')
-  end
-
-  local f
-  if _G[name] and _G[name].GetFrameType and _G[name].GetFrameType() == 'Frame' then
-    f = _G[name]
-    print('found existing table |cFFFFFF00' .. name .. '|r')
-  else
-    print('creating statusbar '.. name, config.anchor, parent:GetName(), config.anchorTo, config.posX, config.posY)
-  end
-
-
-  f = CreateFrame('Frame', name, UIParent)
-  f.db = config
-
-  -- state vars
-  f.combat = InCombatLockdown()
-  f.min = 0
-  f.max = 1
-  f.value = 0
-  f.percent = 0
-  f.alpha = f.combat and config.alpha or config.alpha_ooc
-  f:SetAlpha(f.combat and config.alpha or config.alpha_ooc)
-  f:SetPoint(config.anchor, config.parent, config.anchorTo, config.posX, config.posY)
-  f:SetSize(config.width, config.height)
-  f:SetFrameStrata(config.strata)
-  self:CreateStatusTextures(f, config)
-
-  f.throttle_rate = 0.0166666666 -- ~60fps
-  f.throttle_point = GetTime()
-
-  -- default handler
-  f.Update = T.Bar_Update
-  f.UpdateText = T.Bar_UpdateText
-  T:Bar_SetUpdateHandler(f, T.Bar_Update)
-
-  return f
-end
-
-function T:CreateStatusTextures(region, c)
-  c = c and c or region.db
-  print('STATUSTEX_make', region:GetName(), c.background_texture, c.background_color, c.foreground_texture, c.foreground_color)
-  region.background = region:CreateTexture('background', 'BACKGROUND')
-  region.background:SetAllPoints(region)
-  if c.background_texture then
-    region.background:SetTexture(LSM:Fetch('statusbar', c.background_texture))
-    region.background:SetVertexColor(unpack(c.background_color))
-  else
-    region.background:SetTexture(unpack(c.background_color))
-  end
-  region.background:SetBlendMode(c.background_blend)
-
-  region.foreground = region:CreateTexture('foreground', 'ARTWORK')
-  region.foreground:SetPoint('TOPLEFT', region, 'TOPLEFT', 0-c.foreground_inset, c.foreground_inset)
-  region.foreground:SetPoint('BOTTOMRIGHT', region, 'BOTTOMRIGHT', c.foreground_inset, 0-c.foreground_inset)
-  if c.foreground_texture then
-    region.foreground:SetTexture(LSM:Fetch('statusbar', c.foreground_texture))
-    region.foreground:SetVertexColor(unpack(c.foreground_color))
-  else
-    region.foreground:SetTexture(unpack(c.foreground_color))
-  end
-  region.foreground:SetBlendMode(c.foreground_blend)
-end
-
-function T:CreateStatusIcon(region, c)
-  local file = type(c) == 'string' and c or region.icon
-  if type(c) ~= 'table' then
-    c = region.db
-  end
-  print('STATUSICON', region:GetName(), file, c.icon_show)
-
-end
-
--- Sets the OnUpdate handler within a timing scheme
-function T:Bar_SetUpdateHandler(bar, func)
-  bar:SetScript('OnUpdate', function(bar)
-    if GetTime() < bar.throttle_point then
-      return
-    end
-
-    bar.throttle_point = bar.throttle_point +  bar.throttle_rate
-    if type(func) == 'string' then
-      if type(bar[func]) ~= 'function' then
-        error('TurokBar:SetUpdateScript(); string "' ..func.. '" is not a valid method name')
-        return
-      end
-    elseif type(func) ~= 'function' then
-      error('TurokBar:SetUpdateScript(function or string); got '.. type(func) .. 'instead')
-      return
-    end
-
-    func(bar)
-  end)
-end
-
--- Default update loop
-function T:Bar_Update (value, min, max)
-
-  if value ~= nil then -- could be 0
-    self.value = value
-  end
-  if (min ~= nil and max ~= nil) then
-    if self.isTimer then
-      self.duration = min
-      self.endTime = max
-    else
-      self.min = min
-      self.max = max
-      self.duration = max
-    end
-  end
-
-  if self.combat ~= InCombatLockdown() then
-    self.combat = InCombatLockdown()
-    if self.combat then
-      self.fadeTo = self.db.alpha
-      self.fadeDuration = FADE_IN_TIME
-    else
-      self.fadeTo = self.db.alpha_ooc
-      self.fadeDuration = FADE_OUT_TIME
-      end
-  end
-
-  -- we need the time for something
-  if self.isTimer or self.fadeTo ~= nil then
-    local time = GetTime()
-
-    -- start doing fade animation things
-    if self.fadeTo ~= nil then
-
-      if not self.fadeFrom then
-        self.fadeFrom = self:GetAlpha()
-      end
-
-      -- fadeDuration missing, fill it in
-      if not self.fadeDuration then
-        self.fadeDuration = self.fadeFrom > self.fadeTo and FADE_OUT_TIME or FADE_IN_TIME
-      end
-
-      -- fadeTime missing,
-      if not self.fadeTime then
-        local fadeRatio = 1
-        if self.fadeFrom ~= self.alpha then
-          fadeRatio = (self.fadeTo - self.fadeFrom) / (self.fadeTo - self.alpha) -- target delta
-        end
-        --print(fadeRatio, self.fadeDuration)
-        self.fadeTime = time + fadeRatio * self.fadeDuration -- fixed rate of change
-      end
-
-      -- are we done?
-      if time >= self.fadeTime then
-        self:SetAlpha(self.fadeTo)
-        self.alpha = self.fadeTo
-        self.fadeTo = nil
-        self.fadeFrom = nil
-        self.fadeDuration = nil
-        self.fadeTime = nil
-      else --nope
-        local remaining = (self.fadeTime - time) / self.fadeDuration
-        local fadeTotal = (self.fadeTo - self.fadeFrom)
-        --print(self.fadeTo - (fadeTotal * remaining), fadeTotal * remaining)
-        self:SetAlpha(self.fadeTo - (fadeTotal * remaining))
-      end
-    end
-
-    -- termination check
-    if self.isTimer then
-      if time >= self.endTime and self.fadeTo == nil then
-        self:Hide()
-        return
-      else
-        self.percent = (self.endTime - time) / self.duration
-      end
-    end
-
-  else
-    self.percent = (self.value - self.min) / (self.max - self.min)
-  end
-
-  self:UpdateText()
-  self.foreground:SetPoint('BOTTOMRIGHT', self, 'BOTTOMLEFT', self.db.width * self.percent + self.db.foreground_inset, 0-self.db.foreground_inset)
-end
-
-function T:Bar_UpdateText()
-  local c = {self:GetChildren()}
-  for _, rl in ipairs(c) do
-    if rl.text then
-      rl.format = self.db.label_string
-      rl:Update()
-    end
-  end
-end
-
--- addon:AddLabel
--- Constructs a text display and adds it to that region
---
-function T:AddLabel (region, config, labelname)
-  assert(region:IsObjectType('Frame'), "T:CreateLabel(table, table [, string])")
-  labelname = (labelname or string.format('%x', GetTime() % 1000000))
-  print('assigning frame for text object |cFFFF9922'..labelname..'|r to '..region:GetName())
-
-  local lf = CreateFrame('Frame', region:GetName()..'_'..labelname, region)
-  local ft = lf:CreateFontString(labelname, 'OVERLAY')
-  local lx, ly, lp = 0,0, config.label_point
-  if config.label_inset ~= 0 and lp ~= 'CENTER' then
-    lx = config.label_inset * inset_factor[lp][1]
-    ly = config.label_inset * inset_factor[lp][2]
-  end
-
-  lf:SetPoint(lp, region, lp, lx, ly)
-  lf:SetSize(region:GetWidth(), region:GetHeight())
-  lf:SetFrameStrata(config.label_strata)
-
-  ft:SetFont(LSM:Fetch('font', config.label_font), config.label_size, config.label_outline)
-  ft:SetAllPoints(lf)
-  ft:SetJustifyH(config.label_justifyH or (config.label_point:find('LEFT') and 'LEFT' or (config.label_point:find('RIGHT') and 'RIGHT' or 'CENTER')))
-  ft:SetJustifyV(config.label_justifyV or (config.label_point:find('TOP') and 'TOP' or (config.label_point:find('BOTTOM') and 'BOTTOM' or 'MIDDLE')))
-  ft:SetTextColor(1, 1, 1)
-  ft:SetText('SET ME!')
-
-  lf.Update = region.isTimer and T.Label_UpdateFormat or T.Label_Update
-  lf.SetJustifyH = function(self, justify) ft:SetJustifyH(justify) end
-  lf.SetText = function(self, text) ft:SetText(text) end
-
-  lf.text = ft
-  region[labelname] = lf
-  return lf
-end
-
--- one of two possible assignments for label:Update()
-function T:Label_UpdateFormat()
-  local region = self:GetParent()
-  if self.format then
-    self.value = string.gsub(self.format, '%%p', string.format('%.1f', region.value))
-    self.value = string.gsub(self.value, '%%d', region.duration)
-    if region.name then
-      self.value = string.gsub(self.value, '%%n', region.name)
-    end
-  end
-  self.text:SetText(self.value)
-end
-function T:Label_Update()
-  self.text:SetText(self:GetParent().value)
-end
-
--- a/Lost.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
--- User: Krakyn
--- Created: 12/14/2015 3:03 PM
-local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
-local TL = 'Lost'
-local print = function(...) _G.print(TL, ...) end
-local LSM = LibStub("LibSharedMedia-3.0")
-local mod = T:NewModule(TL, "AceTimer-3.0")
-local time = _G.ct
-local spell_trunc = {}
-local inv = T.anchor_inverse
-local d = T.direction_coord
-local a = T.anchor_direction
-
-mod.encounter_start = {
-  ['Aspect'] = {
-    type = 'buff',
-
-    Test = function()
-      local res = UnitBuff('player', 'Aspect of the Pack', nil, 'PLAYER')
-      return (res ~= nil)
-    end,
-    sound = LSM:Fetch('sound', 'Wilhelm Scream'),
-  },
-  ['Rune'] = {
-    Test = function()
-      local res = UnitBuff('player', 'Hyper Augmentation', nil, 'PLAYER')
-      return (res ~= nil)
-    end
-  }
-}
-
-function mod:OnEnable()
-  self.raidbuffs_active = {}        -- active raid buffs
-  self.raidbuffs_text = {}   -- raid buff text
-  self.raidbuffs_avail = {}  -- availability info
-  self.raidbuffs_display = {} -- buff list ordering info
-  self.unit_cache = {}        -- remember GUIDs we've seen before
-  self.raidbuffs_shown = NUM_LE_RAID_BUFF_TYPES
-
-
-  local db = _G.TurokData
-  db.raidbuff = {
-    parent = 'UIParent',
-    anchor = 'BOTTOMRIGHT', anchorTo = 'BOTTOMRIGHT',
-    posX = -300, posY = 300,
-    height = 24*9, width = 72,
-    raidbuff_width = 72,
-    raidbuff_height = 24,
-    label_size = 11,
-    label_font = 'ArchivoNarrow-Bold',
-  }
-
-
-
-  local rw = CreateFrame('Frame', 'TkRaidWatch', UIParent)
-  local c =  db.raidbuff
-  self.raidbuffs_frame = rw
-  rw:SetPoint(c.anchor, c.parent, c.anchorTo, c.posX, c.posY)
-  rw:SetSize(c.width, c.height)
-  rw:SetMovable(true)
-  local bd = rw:CreateTexture()
-  bd:SetTexture(1,1,1,1)
-  bd:SetGradient('VERTICAL', 0,0,0,1,1,1)
-  bd:SetBlendMode('MOD')
-  bd:SetPoint('TOPLEFT',rw,'TOPLEFT', -2, 2)
-  bd:SetPoint('BOTTOMRIGHT',rw,'BOTTOMRIGHT', 2, -2)
-
-  -- seed raid buff analyzer assets
-  self.num_raidbuff_columns = math.floor(c.width / c.raidbuff_width)
-  for i = 1, 9 do
-    local icon = rw:CreateTexture('TkRaidWatchButton'..i, 'ARTWORK')
-    icon:SetSize(c.raidbuff_width,c.raidbuff_height)
-    icon:SetTexCoord(0.15, 0.85, 0.15, 0.85)
-
-
-    self.raidbuffs_active[i] = icon
-    rw:EnableMouse(true)
-    rw:SetScript('OnMouseDown', function(self) self:StartMoving() end)
-    rw:SetScript('OnMouseUp', function(self) self:StopMovingOrSizing() end)
-
-    local text = rw:CreateFontString('TkRaidWatchText'.. i, 'OVERLAY')
-    text:SetPoint('CENTER', icon, 'CENTER')
-    text:SetFont(LSM:Fetch('font', c.label_font), c.label_size, 'OUTLINE')
-    text:SetText(i)
-    self.raidbuffs_text[i] = text
-  end
-
-  db.raidevent = {}
-
-
-  self:RegisterEvent('PARTY_MEMBERS_CHANGED')
-  self:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED')
-  self:RegisterEvent('ENCOUNTER_START')
-  self:RegisterEvent('INSPECT_READY')
-  self:RegisterEvent('UNIT_AURA', 'RaidBuffScan')
-
-  self:RaidBuffScan()
-  self:RosterScan()
-end
-function mod:PLAYER_REGEN_ENABLED(e,...) end
-function mod:PLAYER_REGEN_DISABLED(e, ...) end
-
-function mod:INSPECT_READY()
-end
-function mod:PLAYER_SPECIALIZATION_CHANGED(e,unit)
-  local specID
-  print(e, unit)
-  if unit == 'player' then
-    specID = GetSpecializationInfo(GetSpecialization())
-  else
-    --NotifyInspect(unit)
-    specID = GetInspectSpecialization()
-  end
-
-  print(GetSpecializationInfoByID(specID))
-  self:RosterScan()
-end
-
-function mod:PARTY_MEMBERS_CHANGED(e, ...)
-  if IsInRaid() or IsInGroup() then
-    self.raidbuffs_frame:Show()
-    self:RaidBuffScan()
-  end
-end
-
-function mod:ENCOUNTER_START(e,...)
-  for k, v in pairs(self.encounter_start) do
-    local test, info = v.Test()
-    if test == true then
-      print(v.type, k, 'doing a thing')
-    else
-      print(v.type, k, info)
-    end
-  end
-end
-
--- Updates available raid/party buffs
-function mod:RaidBuffScan(unit)
-  if self.hiding then
-    return
-  end
-  local c = T.db.raidbuff
-
-  -- search for unit data
-  -- update raidbuffs
-
-  -- set our number of things to track
-
-  local k = 1
-  for i = 1, NUM_LE_RAID_BUFF_TYPES do
-    local rb = self.raidbuffs_active[i]
-    local rt = self.raidbuffs_text[i]
-    local name, rank, texture, duration, expiration, spellId, slot = GetRaidBuffTrayAuraInfo(i)
-    if name then
-      rb:Hide()
-      rt:Hide()
-      self.raidbuffs_display[i] = nil
-    else
-        rb:Show()
-        rt:Show()
-        if self.raidbuffs_avail[i] then
-          rb:SetTexture(0.5,0.5,0.5,0.1)
-          rb:SetBlendMode('MOD')
-        else
-          rb:SetTexture(1,0.2,0,0.5)
-          rb:SetBlendMode('ADD')
-        end
-        self.raidbuffs_text[i]:SetText(string.sub(_G['RAID_BUFF_'..i],0,2))
-
-        if not self.raidbuffs_display[i] or self.raidbuffs_display[i] ~= k then
-          self.raidbuffs_display[i] = k
-          local pn = k-1                                     -- need (n-1) for lua grid math
-          local py = math.floor(pn / self.num_raidbuff_columns) * c.raidbuff_height
-          local px = (pn * c.raidbuff_width) % c.width      -- x-offset
-          print('buff slot '..i..' (draw slot '..k..')', pn, py, px)
-          rb:SetPoint('BOTTOMLEFT', TkRaidWatch, 'BOTTOMLEFT', px, py)
-        end
-
-        k = k + 1
-    end
-  end
-end
-
-function mod:RosterScan()
-  local lim = 1
-  if IsInRaid() then
-    lim = 40
-  elseif IsInGroup() then
-    lim = 5
-  end
-
-  for i = 1, lim do
-      local name, rank, subgroup, level, class, fileName, zone, online, isDead, role, isML = GetRaidRosterInfo(i)
-      local realm
-      if name then
-        if string.find(name,'-') then
-          name, realm = string.match(name, "(.+)-(.+)")
-        else
-          realm = GetRealmName()
-        end
-
-        local GUID = UnitGUID('raid' .. i)
-        if not self.unit_cache[GUID] then
-          self.unit_cache[GUID] = {
-            name = name,
-            realm = realm,
-          }
-        end
-
-        pl = self.unit_cache[GUID]
-
-
-        print(i, name, class, role)
-      end
-
-  end
-
-end
\ No newline at end of file
--- a/Spirit.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
--- User: Krakyn
--- Created: 12/8/2015 7:30 PM
-local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
-local TL = 'Spirit'
-local print = function(...) _G.print(TL, ...) end
-local LSM = LibStub("LibSharedMedia-3.0")
-local mod = T:NewModule("Spirit", "AceTimer-3.0")
-local time = _G.ct
-local auras = {
-  168811, -- sniper training
-  168809, -- sniper training: recently moved
-  13159,           -- aspect of the pack,
-  187615, -- Maalus
-}
-local spells = {
-  3045, -- rapid fire,
-  26297, -- berserking,
-  121818, -- stampede,
-
-}
-local FRAME_PREFIX = 'TkSpirit'
-local FADE_TIME = 0.5
--- local repetitive tasks
-local function FindSpirit(id)
-  local db = T.db
-  local c = db.spirit
-  local isIcon = false
-  if db.spirit.progressbar[id] then
-    c = db.spirit.progressbar[id]
-  elseif db.spirit.icon[id] then
-    c = db.spirit.icon[id]
-    isIcon = true
-  end
-  local name, rank, icon, castingTime, minRange, maxRange, spellID = GetSpellInfo(id)
-  local s = {
-    name = name,
-    rank = rank,
-    icon = icon,
-    castingTime = castingTime,
-    minRange = minRange,
-    maxRange = maxRange,
-    spellID = spellID
-  }
-
-  return s, c, isIcon
-end
-
-function mod:OnEnable()
-  local db = _G.TurokData
-  self.aura_watch = {}
-  self.cooldown_watch = {}
-  self.item_watch = {}
-  self.myGUID = UnitGUID('player')
-
-
-  db.spirit = {
-    foreground_color = {1, 1, 1, 0.7},
-    foreground_texture = 'Cilo',
-    foreground_blend = 'BLEND',
-    foreground_inset = -1,
-    background_color = {0, 0, 0, 0.7},
-    background_blend = 'BLEND',
-    background_texture = 'Cilo',
-    icon_show = true,
-    width = 250, height = 20,
-    anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
-    posX = 0, posY = -150,
-    label_color = {1, 1, 1, 1},
-    label_font = 'turok',
-    label_size = 14,
-    label_inset = -2,
-    label_point = 'LEFT',
-    label_outline = 'OUTLINE',
-    label_string = '%n %p',
-    label_strata = 'HIGH',
-    expire_sound = LSM:Fetch('sound'),
-    strata = 'LOW',
-    icon = {
-      [13159] = {  -- Aspect of the Pack
-        posX = 20, posY = 20, width = 140, height = 140,
-      },
-      [3045] = {
-        parent = 13159, anchor = 'RIGHT', anchorTo = 'LEFT',
-        posx = 0, posY = 0, width = 64, height = 64,
-      }
-    },
-    progressbar = {
-      [168811] = { -- Sniper Training (duration)
-        background_color = {0,0,0,0},
-        foreground_color = {1,0,0,1},
-        anchor = 'BOTTOMLEFT', parent = 'TkFocusBar', anchorTo = 'TOPLEFT',
-        posX = 0, posY = 0, height = 16, width = 250,
-        label_string = '',
-        label_point = 'TOPLEFT',
-        strata = 'LOW',
-        attach = {{
-          width_relative = 0.5,
-          height_relative = 1,
-          background_color = {1,0,0,1},
-          foreground_color = {1,1,0.5, 1},
-          background_blend = 'ADD',
-          foreground_blend = 'ADD',
-
-          anchor = 'LEFT',
-          anchorTo = 'LEFT',
-          strata = 'MEDIUM',
-        }}
-      },
-      [168809] = { -- Sniper Training: Recently Moved
-        background_color = {0,0,0,0 },
-        foreground_color = {1,1,1,1},
-        foreground_blend = 'ADD',
-        anchor = 'BOTTOMLEFT', parent = 'TkFocusBar', anchorTo = 'TOPLEFT',
-        strata = 'HIGH',
-        posX = 0, posY = 0, height = 16, width = 125,
-        icon_show = false,
-        label_string = '',
-        exceptions = {
-          function(aura)
-            return (mod.aura_watch[168811].expires < aura.expires)
-          end
-        },
-      },
-    },
-  }
-
-  for _, id in pairs(auras) do
-    -- store spellinfo fetch
-    self.aura_watch[id] = self:CreateSpiritFrame(id)
-    self.aura_watch[id].count = 0
-    self.aura_watch[id].duration = 0
-    self.aura_watch[id].expires = 0
-    self.aura_watch[id].caster = 0
-    print('AURA', id, self.aura_watch[id].name)
-  end
-
-  for _, id in ipairs(spells) do
-    self.cooldown_watch[id] = self:CreateSpiritFrame(id)
-    print('COOLDOWN', id, self.cooldown_watch[id].name)
-  end
-
-  self:SpiritScan()
-  self:RegisterEvent('UNIT_AURA')
-  self:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
-  self:RegisterEvent('PLAYER_REGEN_DISABLED')
-  self:RegisterEvent('PLAYER_REGEN_ENABLED')
-end
-
--- StatusBar factory
-function mod:CreateSpiritFrame(id)
-  local s, c, isIcon = FindSpirit(id)
-
-  -- how much frame do we need?
-  local f
-  if isIcon then
-    f = CreateFrame('Frame', FRAME_PREFIX..id, UIParent)
-    f.db = c
-    function f:Update() end
-  else
-    f = T:CreateBar('TkAuraBar'..id, c)
-    f.isTimer = true
-    if (c.label_string ~= '') then
-      print('has label, add it', id)
-      T:AddLabel(f, c)
-      end
-  end
-  f:Hide()
-
-  -- general display
-  f:SetPoint(c.anchor, c.parent, c.anchorTo, c.posX, c.posY)
-  f:SetSize(c.width, c.height)
-  f:SetFrameStrata(c.strata)
-
-  -- icon?
-  f.icon = s.icon
-  T:CreateStatusIcon(f)
-
-  -- attachment frames?
-  if c.attach then
-    for i, e in ipairs(c.attach) do
-      local ef = CreateFrame('Frame', 'TkExtra'..id, f)
-      f[i] = ef
-      ef:SetPoint(e.anchor, f, e.anchorTo)
-      ef:SetSize(c.width * e.width_relative or e.width, c.height * e.height_relative or e.height)
-      ef:SetFrameStrata(e.strata or c.strata)
-      T:CreateStatusTextures(ef, e)
-    end
-  end
-
-  -- setup suppression checking
-  if c.exceptions then
-    function s:ExceptionCheck ()
-      for i, func in ipairs(c.exceptions) do
-        if not func(self) then
-          return false, i
-        end
-      end
-      return true
-    end
-  else
-    function s:ExceptionCheck () return true end
-  end
-
-  -- access linkage
-  f.format = c.label_string
-  f.spirit = s
-  f.name = s.name
-  s.frame = f
-  return s
-end
-
-function mod:UNIT_AURA(e, unit)
-  if unit == 'player' then
-    self:SpiritScan()
-  end
-end
-
--- Updates aura watches
-function mod:SpiritScan()
-  local db = _G.TurokData
-  for id, aura in pairs(self.aura_watch) do
-    local c = aura.conf
-    local f = aura.frame
-
-    local name, _, _, count, _, duration, expires, caster = UnitAura('player', aura.name)
-
-    if name then
-      aura.duration = duration
-      aura.expires = expires
-      aura.caster = caster
-      aura.count = count
-      local test, i = aura:ExceptionCheck()
-      print(name, duration, expires)
-      if not test then
-        print('suppressing '..aura.name..' (failed test #'..i..')')
-      else
-        f.name = name
-        f:Update(nil, duration, expires)
-        f:Show()
-      end
-    end
-  end
-end
-
-function mod:PLAYER_REGEN_ENABLED()
-  for id, aura in pairs(self.aura_watch) do
-    print('non-combat alpha for #', id, aura.name, 'is', aura.frame.db.alpha_ooc)
-    aura.frame.fadeTo = aura.frame.db.alpha_ooc
-    aura.frame.fadeDuration = FADE_TIME
-  end
-end
-
-function mod:PLAYER_REGEN_DISABLED()
-  for id, aura in pairs(self.aura_watch) do
-    print('combat alpha for #', id, aura.name, 'is', aura.frame.db.alpha)
-    aura.frame.fadeTo = aura.frame.db.alpha
-    aura.frame.fadeDuration = FADE_TIME
-  end
-end
-
--- This is the most reliable way of catching item and spell uses, other events are delayed or too vague to be useful
-function mod:COMBAT_LOG_EVENT_UNFILTERED(e, ...)
-  local timestamp, combatEvent, hideCaster, sourceGUID, sourceName, sourceFlags, sourceRaidFlags, destGUID, destName, destFlags, destRaidFlags =
-  ...; -- Those arguments appear for all combat event variants.
-  local eventPrefix, eventSuffix = combatEvent:match("^(.-)_?([^_]*)$");
-  if eventPrefix ~= 'SPELL_' or sourceGUID ~= self.myGUID then
-    return
-  end
-
-  local spellid, name, count, type = select(13, select('#', ...), ...)
-  local kc = ''
-  for i=1,4 do
-    kc = kc .. string.format('%X',((string.byte(eventSuffix,i) % 8) + 8))
-  end
-  print('|cFFFF'..kc..eventSuffix..'|r', sourceName, destName, spellid, name)
-end
\ No newline at end of file
--- a/Tek.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,404 +0,0 @@
-addon, T_ = ...
--- User: Krakyn
--- Created: 12/4/2015 11:17 PM
-local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
-local db = T.db
-local TL = 'Tek'
-local mod = T:NewModule("Tek")
-local print = function(...)
-  _G.print('Tek', ...)
-end
-local time = _G.ct
-
--- events & units
-local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'}
-local TRACKED_EVENTS = {'UNIT_SPELLCAST_START', 'UNIT_SPELLCAST_DELAYED', 'UNIT_SPELLCAST_STOP', 'UNIT_SPELLCAST_FAILED', 'UNIT_SPELLCAST_FAILED_QUIET',
-  'UNIT_SPELLCAST_INTERRUPTED', 'UNIT_SPELLCAST_SUCCEEDED', 'UNIT_SPELLCAST_CHANNEL_START', 'UNIT_SPELLCAST_CHANNEL_STOP', 'UNIT_SPELLCAST_CHANNEL_UPDATE' }
-
--- animations are a little different here
-local FADE_OUT_TIME, FADE_IN_TIME = 1000, 200
-
--- values MUST be in chronological order
-local CHANNEL_START, CAST_START, CAST_INTERRUPTED, CAST_SUCCEEDED, CAST_STOPPED, CAST_FAILED, CHANNEL_STOPPED = 1, 2, 3, 4, 5, 6, 7
-local TEXTURE_SUFFIX = {
-  [CHANNEL_START] = '_channeling',
-  [CAST_START] = '_casting',
-  [CAST_INTERRUPTED] = '_interrupted',
-  [CAST_SUCCEEDED] = '_finished',
-  [CAST_STOPPED] = '_failed',
-  [CAST_FAILED] = '_failed',
-  [CHANNEL_STOPPED] = ''
-}
-
--- defaults
-db.castbar = {
-  --T.defaults.castbar = {
-  width = 300, height = 24,
-  anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
-  posX = 0, posY = 0,
-  foreground_casting = {0.80784313725, 0.87843137254, 0.0156862745, 1},
-  background_casting = {0,0,0,0.4},
-  background_interrupted = {1,0,0,1},
-  background_failed = {0.60784313725, 0.87843137254, 0.0156862745, 1},
-  foreground_interrupted = {1, 0.5, 0, 1},
-  foreground_failed = {0,0,0,1},
-  foreground_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1},
-  background_finished = {0.60784313725, 0.87843137254, 0.0156862745, 1},
-  foreground_inset = -1,
-  fill_direction = 'RIGHT',
-  fill_inverse = false,
-  glow_texture = "Tooltip-BigBorder",
-  glow_size = 2,
-  spark_texture = "Tooltip-BigBorder",
-  spark_size = 2,
-  ['player'] = {
-    width = 400, height = 36,
-    anchor = 'TOP', parent = 'TkFocusBar', anchorTo = 'BOTTOM',
-    posX = 0, posY = 2,
-  },
-  ['target'] = {
-    width = 400, height = 36,
-    anchor = 'BOTTOMLEFT', parent = 'TkplayerCastBar', anchorTo = 'TOPRIGHT',
-    posX = -30, posY = 4,
-  },
-  ['focus'] = {
-    width = 200, height = 36,
-    anchor = 'TOPLEFT', parent = 'TkplayerCastBar', anchorTo='BOTTOMRIGHT',
-    posX = 2, posY = -2
-  },
-  ['pet'] = {
-    width = 300, height = 24,
-    anchor = 'TOPRIGHT', parent = 'TkFocusBar', anchorTo='TOPLEFT',
-    posX = -2, posY = 0
-  },
-}
-
-local inv = T.anchor_inverse
-local d = T.direction_coord
-local a = T.anchor_direction
-local c = {}
--- c[unit]=nil is used by frame update, thus any indexing from a valid frame is implied to be for new casting info
-setmetatable(c, {
-  __index = function(t,k)
-    t[k] = {}
-    return t[k]
-  end
-})
-
-function mod:OnEnable()
-  local db = T.db
-
-  self.casting = c
-  self.castbar = {}
-
-
-  for _, unit in pairs(TRACKED_UNITS) do
-    local cdb = db.castbar[unit]
-    print('|cFFFF44FF' .. unit .. '|r castbar creation')
-
-
-    -- State info
-    c[unit] = {}
-
-    -- Set frames
-    local fn = 'Tk' .. unit .. 'CastBar'
-    self.castbar[unit] = T:CreateBar(fn, cdb)
-    local pc = self.castbar[unit]
-    pc:Hide()
-    pc:SetAlpha(0)
-
-    T:AddLabel(pc, cdb, 'spelltext')
-    local casttime = T:AddLabel(pc, cdb, 'casttime')
-    pc.foreground:SetWidth(0)
-
-    casttime:SetPoint('RIGHT', pc, 'RIGHT')
-    casttime:SetJustifyH('RIGHT')
-
-    if unit == 'player' then
-      local lb = pc:CreateTexture('latency', 'OVERLAY')
-      local lt = T:AddLabel(pc, cdb, 'latency')
-      lt:SetPoint('TOPRIGHT', pc.casttime, 'BOTTOMRIGHT')
-      lt:SetJustifyH('RIGHT')
-      pc.latency = lb
-      lb.text = lt
-    end
-
-    pc.transpt = a[cdb.fill_direction]
-    pc.xtrans = function() return math.min((cdb.width + cdb.foreground_inset) * pc.percent * d[pc.transpt][1], cdb.width) end
-    pc.ytrans = function() return math.min((cdb.height + cdb.foreground_inset) * pc.percent * d[pc.transpt][2], cdb.height) end
-
-    pc.movement_x =  1
-    pc.moving_end = 'LEFT'
-
-    local glow = pc:CreateTexture('glow', 'OVERLAY')
-    glow:SetTexture(CASTBAR_TAILGLOW)
-    glow:SetPoint('TOPLEFT', pc, 'TOPLEFT', -5, 5)
-    glow:SetPoint('BOTTOMRIGHT', pc, 'BOTTOMRIGHT', 5, -5)
-    pc.glow = glow
-
-    local icon = pc:CreateTexture('icon', 'ARTWORK')
-    icon:SetPoint('RIGHT', pc, 'LEFT', -2, 0)
-    icon:SetWidth(pc.foreground:GetHeight())
-    icon:SetHeight(pc.foreground:GetHeight())
-    icon:SetTexture(0,0,0,1)
-    pc.icon = icon
-
-    pc.unit = unit
-    T:Bar_SetUpdateHandler(pc, self.TekUpdate)
-  end
-
-  -- Casting table events
-  for i, event in pairs(TRACKED_EVENTS) do
-    print('listening to |cFF00FFFF' .. event .. '|r')
-    self:RegisterEvent(event, 'TekEvent')
-  end
-
-  -- Extra events
-  self:RegisterEvent('PLAYER_TARGET_CHANGED')
-  self:RegisterEvent('PLAYER_FOCUS_CHANGED')
-
-  -- kill default casting bar
-  -- T.cbscripts = {CastingBarFrame:GetScript('OnUpdate'), CastingBarFrame:GetScript('OnEvent')}
-  CastingBarFrame:SetScript('OnUpdate', nil)
-  CastingBarFrame:SetScript('OnEvent', nil)
-  CastingBarFrame:Hide()
-end
-
--- Update handler for castbar
-function mod:TekUpdate()
-  local glow = self.glow
-  local foreground = self.foreground
-  local background = self.background
-  local latency = self.latency
-  local time = GetTime() * 1000
-  local spelltext = self.spelltext
-  local timetext = self.timetext
-  local cdb = self.db
-  if mod.casting[self.unit] ~= nil then
-
-    local u = self.unit
-    local s = mod.casting[u]
-    local alpha = self:GetAlpha()
-
-    -- is something casting at all?
-    if s.casting <= CAST_START then
-
-      -- start = true whenever a new spell cast could be active (i.e. target switching)
-      if s.init  then
-        print('|cFFDD77DD'..u..'|r init_cast (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')')
-        print(self:GetName(), self:IsVisible(), self:IsShown())
-
-        if u == 'player' then
-          self.latency.text:SetText(s.latency)
-        end
-
-        -- update translation point
-        if s.casting == CHANNEL_START and not cdb.fill_inverse then
-          self.transpt = inv[a[cdb.fill_direction]]
-        else
-          self.transpt = a[cdb.fill_direction]
-        end
-
-        -- update frame contents
-        foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color))
-        background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color))
-
-        self.icon:SetTexture(s.texture)
-        spelltext:SetText(s.displayName)
-        self.duration = s.endTime - s.startTime
-        self.fadeIn = s.startTime + (1-alpha) * FADE_IN_TIME
-        self.fadeOut = s.endTime + FADE_OUT_TIME -- needs to exist for target changes
-
-        print(s.startTime, self.fadeIn, self.fadeIn - s.startTime)
-        -- clear 'start'
-        s.init = nil
-      end
-
-    -- if we're checking this and start was never flipped, then something happened
-      if time <= self.fadeIn then
-          alpha = 1- ((self.fadeIn - time) / FADE_IN_TIME)
-          self:SetAlpha(alpha)
-      elseif alpha ~= 1 then
-          self:SetAlpha(1)
-      end
-      self.value = time - s.startTime
-      self.percent  = self.value / self.duration
-      self.casttime:SetText(format("%.1f", self.value / 1000))
-
-    -- s.casting is nil when the spellcast has finished
-    else
-    -- something set a term flag
-      if s.casting < CHANNEL_STOPPED and s.fade then
-        print(TL, '|cFFDD77DD'..u..'|r init_fadeout (spell='..s.displayName..', startTime='..s.startTime..', endTime='..s.endTime..', channel=',s.channel,')')
-        self.fadeOut = time + FADE_OUT_TIME
-        foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] and cdb['foreground' .. TEXTURE_SUFFIX[s.casting]] or cdb.foreground_color))
-        background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[s.casting]] and cdb['background' .. TEXTURE_SUFFIX[s.casting]] or cdb.background_color))
-        s.fade = nil
-        self.value = self.duration
-        self.percent = 1
-      end
-
-      if time < self.fadeOut then
-        alpha = (self.fadeOut - time) / FADE_OUT_TIME
-        self:SetAlpha(alpha)
-      else
-        alpha = 0
-        self:Hide()
-        self:SetAlpha(alpha)
-        self.casttime:SetText(nil)
-        self.spelltext:SetText(nil)
-        self.debugged = false
-        self.percent = 0
-        if u == 'player' then
-          self.latency.text:SetText(nil)
-        end
-        mod.casting[self.unit] = nil
-        print('|cFFDD77DD'..u..'|r work complete, hiding...')
-      end
-    -- hide and wait until we're pulled out again
-    end
-    self.foreground:SetPoint('RIGHT', self, self.transpt, self.xtrans(), self.ytrans())
-  end
-end
-
-
--- event stub, filters out unwanted cast events
-function mod:TekEvent(e, unit, ...)
-  if not self.castbar[unit] then
-    return
-  end
-  if not self[e] then
-    error('No method signature for event ' .. tostring(e))
-  end
-  print('popped |cFF00FFFF' .. e .. '|r: ', unit, ...)
-  self[e](self, e, unit, ...)
-end
-
-function mod:PLAYER_TARGET_CHANGED (e, cause)
-  print(e)
-  self:UpdateUnit('target')
-end
-function mod:PLAYER_FOCUS_CHANGED (e, cause)
-  print(e)
-  self:UpdateUnit('focus')
-end
-function mod:UpdateUnit(unit)
-  if UnitCastingInfo(unit) then
-    mod:UNIT_SPELLCAST_START('UNIT_SPELLCAST_START', unit)
-  elseif UnitChannelInfo(unit) then
-    mod:UNIT_SPELLCAST_CHANNEL_START('UNIT_SPELLCAST_CHANNEL_START', unit)
-  else
-    mod.castbar[unit]:Hide()
-  end
-end
-
--- Spell event handlers
-function mod:UNIT_SPELLCAST_SENT(e, unit, spellname, rank, target)
-  -- triggered an action buttton tied to a cast
-  c[unit].sentTime = math.floor(GetTime() * 1000)
-  print('|cFF44FF44',e,':|r', unit, spellname, c[unit].sentTime)
-  c[unit].spell = spellname
-  c[unit].rank = rank
-  c[unit].target = target
-end
-
-function mod:UNIT_SPELLCAST_START(e, unit) -- Server says: someone started casting
-  local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit)
-  print('casting['..unit..'] state updated (=start): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
-
-  c[unit].castID = castID
-  c[unit].spell = spellname
-  c[unit].rank = rank
-  c[unit].displayName = displayName
-  c[unit].texture = texture
-  c[unit].startTime = startTime
-  c[unit].endTime = endTime
-  c[unit].nonInterruptible = nonInterruptible
-  c[unit].isTradeSkill = isTradeSkill
-  if c[unit].sentTime then
-    c[unit].latency = c[unit].startTime - c[unit].sentTime
-  end
-
-  -- set state and show
-  c[unit].casting = CAST_START
-  c[unit].init = true
-  self.castbar[unit]:Show()
-end
-
-function mod:UNIT_SPELLCAST_DELAYED(e, unit, spellname, rank, castID, target) -- Server says: they're still casting but it'll take longer
-  local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = UnitCastingInfo(unit)
-  print('casting['..unit..'] state updated (=delayed): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
-  c[unit].castID = castID
-  c[unit].channel = false
-  c[unit].startTime = startTime
-  c[unit].endTime = endTime
-  -- just update timing data, frame script will adjust
-end
-
--- set exit states
-function mod:UNIT_SPELLCAST_STOP(e, unit, spellname, rank, castID, target) -- Server says: someone stopped casting for some reason
-  --c[unit].casting = CAST_STOPPED
-end
-
-function mod:UNIT_SPELLCAST_INTERRUPTED(e, unit, spellname, rank, castID, target) -- Server says: someone got interrupted
-  c[unit].casting = CAST_INTERRUPTED
-  c[unit].fade = true
-end
-
-function mod:UNIT_SPELLCAST_SUCCEEDED(e, unit, spellname, rank, castID, target) -- Server says: they stopped because they're done
-
-  if c[unit].castID == castID and c[unit].casting ~= CHANNEL_START then
-    c[unit].casting = CAST_SUCCEEDED
-    c[unit].fade = true
-  end
-end
-
-function mod:UNIT_SPELLCAST_FAILED(e, unit, spellname, rank, castID, target) -- Server says: someone tried to cast something but they weren't allowed
-  if c[unit].castID ~= castID then -- can fire from keybind spam
-    return
-  end
-  c[unit].casting = CAST_FAILED
-  c[unit].fade = true
-end
-
-function mod:UNIT_SPELLCAST_FAILED_QUIET(e, unit, spellname, rank, castID, target) -- Server says: someone tried to cast something but they weren't allowed
-  if c[unit].castID == castID and c[unit].casting == CAST_START then
-    c[unit].casting = CAST_FAILED
-  end
-end
-
-function mod:UNIT_SPELLCAST_INTERRUPTIBLE(e, unit)
-  c[unit].notInterruptible = false
-end
-function mod:UNIT_SPELLCAST_NOT_INTERRUPTIBLE(e, unit)
-  c[unit].notInterruptible = true
-end
-
-function mod:UNIT_SPELLCAST_CHANNEL_START(e, unit, spellname, rank, castID, spellID)
-  local spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible  = UnitChannelInfo(unit)
-  displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author
-  print('casting['..unit..'] state updated (=start, channel): spellname='.. spellname ..', startTime='.. startTime ..', endTime='.. endTime)
-  c[unit].casting = CHANNEL_START
-  c[unit].spellname = spellname
-  c[unit].rank = rank
-  c[unit].displayName = displayName
-  c[unit].texture = texture
-  c[unit].startTime = startTime
-  c[unit].endTime = endTime
-  c[unit].nonInterruptible = nonInterruptible
-  c[unit].isTradeSkill = isTradeSkill
-  c[unit].init = true
-  mod.castbar[unit]:Show()
-end    -- start up
-function mod:UNIT_SPELLCAST_CHANNEL_STOP(e, unit, spellname, rank, castID, spellID)
-  c[unit].casting = CHANNEL_STOPPED
-end
-function mod:UNIT_SPELLCAST_CHANNEL_UPDATE(e, unit, spellname, rank, castID, spellID)
-  spellname, rank, displayName, texture, startTime, endTime, isTradeSkill, nonInterruptible  = UnitChannelInfo(unit)
-  displayName = spellname -- channels sometimes just appear as 'Channeling' according to Quartz author
-  c[unit].channel = true
-end -- recalc
-
-
-
-
--- a/Turok.iml	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module type="LUA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output />
-    <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
-    </content>
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>
\ No newline at end of file
--- a/Turok.lua	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,159 +0,0 @@
-
--- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
-local T = LibStub("AceAddon-3.0"):NewAddon("Turok", "AceConsole-3.0", "AceEvent-3.0")
-local _G = _G
-local rawset = _G.rawset
-_G.Turok = T
-local print = function(...)
-  --_G.print('Core', ...)
-end
-
-
-T.mods = {}
-T:SetDefaultModuleState(true)
-T:SetDefaultModuleLibraries("AceEvent-3.0")
-T:SetDefaultModulePrototype({OnInitialize = function(mod)
-  print('CORE', mod:GetName().. ' found')
-end})
-
--- propagates parameter data around
-function T:LinkTable(parent, child, pname, cname)
-  local mt = {
-    __index = function(t,k)
-      if parent[k] then
-        t[k] = parent[k]
-        end
-      print('|cFFFF8800'.. pname ..'.' .. cname .. '|r.|cFFFFFF00'.. k ..'|r inheriting value at |cFFFF8800' .. pname ..'|r.|cFFFFFF00'.. k..'|r')
-      return parent[k]
-    end,
-    __newindex = function (t, k, v)
-      rawset(t,k,v)
-      if type(v) == 'table' then
-        T:LinkTable(child, v, pname, k)
-        print('|cFFFF8800'.. pname ..'.' .. cname .. '|r.|cFFFFFF00'.. k ..'|r sub-table created')
-      end
-    end
-  }
-  print('|cFFFF8800'.. pname ..'.|cFFFFFF00'.. cname ..'|r sub-tables will be retconned')
-  setmetatable(child, mt)
-  for k, v in  pairs(child) do
-    if type(v) == 'table' then
-      T:LinkTable(child, v, cname, k)
-    end
-  end
-end
-
-function T:OnInitialize()
-
-  local defaults = {
-    background_color = {0,0,0,0},
-    background_blend = 'BLEND',
-    foreground_color = {1,1,1,0.5},
-    foreground_blend = 'BLEND',
-    foreground_inset = -1,
-    width = 250,
-    height = 100,
-    alpha = 1,
-    alpha_ooc = 0.1,
-    strata = 'LOW',
-    label_strata = 'HIGH',
-    label_justifyH = 'LEFT',
-    label_color = {1, 1, 1, 1},
-    label_font = 'turok',
-    label_size = 14,
-    label_inset = -2,
-    label_point = 'LEFT',
-    label_outline = 'OUTLINE',
-    anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
-    focusbar = {
-      foreground_color = {1, 1, 1, 0.7},
-      background_color = {0,0,0,0.8},
-      width = 300, height = 24,
-      posX = 0, posY = -150,
-    },
-  }
-  _G.TurokData = defaults
-  if not _G.TurokData then
-    _G.TurokData = defaults
-  end
-
-  T.db = _G.TurokData
-  local db = T.db
-  for k, v in pairs(db) do
-    if type(v) == 'table' then
-      T:LinkTable(db, v, 'db', k)
-    end
-  end
-  setmetatable(db,
-    {__newindex = function (t, k, v)
-    rawset(t,k,v)
-    if type(v) == 'table' then
-      T:LinkTable(db, v, 'db', k)
-      print('CFG', '|cFFFF0000db|r.|cFF00FFFF' .. k ..'|r created at bottom level')
-    end
-    end})
-
-  options = {
-    type = 'group',
-    name = 'Turok',
-    handler = T,
-    set = function(info,value)
-      local k = db[info[1]]
-      for i = 2, #info-1 do
-        if type(k[i]) ~= 'table' then
-          print('fart')
-        end
-
-        k = k[i]
-      end
-      k[info[#info]] = value
-    end,
-    get = function(info)
-      local k = db[info[1]]
-      for i = 2, #info-1 do
-        if type(k[i]) ~= 'table' then
-          print('fart')
-        end
-
-        k = k[i]
-      end
-      return k[info[#info]]
-    end,
-    desc = '"Dinosaur" Hunter',
-    args = {
-      background_color = {
-        type = 'color',
-        name = 'Background Color',
-        hasAlpha = true,
-      }
-    }
-  }
-  LibStub("AceConfig-3.0"):RegisterOptionsTable('Turok', options, {"tk"})
-
-end
-
-
-function T:OnEnable()
-  local db = _G.TurokData
-
-  print('I... am Turok')
-
-  self.stats = {
-    maxpower = UnitPowerMax('player')
-  }
-
-
-  T.focusbar = T:CreateBar('TkFocusBar', db.focusbar)
-  local fb = T.focusbar
-  T:AddLabel(fb, db.focusbar)
-  fb:Update(UnitPower("player"), 0, UnitPowerMax("player"))
-  T:Bar_SetUpdateHandler(fb, function(self)
-      local pp = UnitPower("player")
-      if pp == T.stats.maxpower and not T.stats.capped then
-        T.stats.capped = true
-      elseif T.stats.capped then
-          T.stats.capped = false
-      end
-      T.Bar_Update(fb, pp)
-  end)
-end
--- a/Turok.toc	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-## Interface: 60200
-## Title: Turok
-## Notes: Dinosaur Huntin'
-## Author: Krakyn
-## Version: 1.0
-## SavedVariables: TurokData
-## OptionalDep: Ace3
-
-Libs\LibStub\LibStub.lua
-Libs\AceConsole-3.0\AceConsole-3.0.xml
-Libs\AceAddon-3.0\AceAddon-3.0.xml
-Libs\AceConfig-3.0\AceConfig-3.0.xml
-Libs\AceEvent-3.0\AceEvent-3.0.xml
-Libs\AceTimer-3.0\AceTimer-3.0.xml
-Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml
-Libs\LibSharedMedia-3.0\lib.xml
-Media\Manifest.lua
-Turok.lua
-Fog.lua
-Tek.lua
-Equip.lua
-Spirit.lua
-Lost.lua
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/.pkgmeta	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,19 @@
+externals:
+    Libs/LibStub:
+        url: svn://svn.wowace.com/wow/libstub/mainline/trunk
+        tag: latest
+    Libs/CallbackHandler-1.0:
+        url: svn://svn.wowace.com/wow/callbackhandler/mainline/trunk/CallbackHandler-1.0
+        tag: latest
+    Libs/AceAddon-3.0:
+        url: svn://svn.wowace.com/wow/ace3/mainline/trunk/AceAddon-3.0
+        tag: latest
+    Libs/LibGroupInSpecT-1.1:
+        Libs/LibGroupInSpecT-1.1: svn://svn.wowace.com/wow/libgroupinspect/mainline/trunk
+
+ignore:
+    - Debug.lua
+    - Turok.iml
+    - .idea
+
+enable-nolib-creation: no
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Init.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,287 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/28/2015 6:40 AM
+--- Core load sequence goes here.
+--@debug@
+local ADDON, TurokEnv = ...
+local _G = _G
+--@end-debug@
+--GLOBALS: Turok, LibStub, GetAddOnMetadata
+local setmetatable, rawset, rawget, pairs, type, unpack, tostring, tinsert = setmetatable, rawset, rawget, pairs, type, unpack, tostring, table.insert
+Turok = LibStub("AceAddon-3.0"):NewAddon('Turok', 'AceEvent-3.0', 'AceTimer-3.0', 'AceConsole-3.0')
+Turok:SetDefaultModuleState(true)
+TurokEnv.Addon = Turok
+TurokEnv.LSM = LibStub("LibSharedMedia-3.0")
+TurokEnv.LGIST = LibStub("LibGroupInSpecT-1.1")
+local T = Turok
+--@debug@
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('DB', ...)
+  end
+end
+local cTypes = { ['function'] = 'FF9922', ['table'] = '00FFAA', ['number'] = '77FF00', ['string'] = '00AAFF', ['hl'] = 'FF0088',  ['field'] = '00FFFF',
+  ['boolean'] = 'CC88FF', ['false'] = 'FF7700', ['true'] = '44FF66', ['nil'] = 'FFFF00',}
+
+local Debuggers = {
+  cType = function(s) return '|cFF' .. tostring(cTypes[type(s)]) .. tostring(s) .. '|r' end,
+  cText = function(s) return '|cFF'..cTypes.string..tostring(s)..'|r' end,
+  cNum  = function(n) return '|cFF'..cTypes.number .. tostring(n) .. '|r' end,
+  cWord = function(w) return '|cFF'..cTypes.table .. tostring(w) .. '|r' end,
+  cKey  = function(k) return '|cFF'..cTypes.field .. tostring(k) .. '|r' end,
+  cPink = function(s) return '|cFF'..cTypes.hl .. tostring(s) .. '|r' end,
+  cBool = function(s) return '|cFF'..cTypes[tostring((not not s))] .. tostring(s) .. '|r' end,
+}
+for fname, func in pairs(Debuggers) do
+  _G[fname] = func
+end
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+--@end-debug@
+
+T.db = {}
+T.versionString = GetAddOnMetadata("Turok", "Version")
+
+function T:OnModuleCreated(mod)
+  if mod.events then
+    print('Module', unpack(mod.events))
+  end
+end
+--- RegisterEvent embedded to module tables
+function T:RegisterCallback(event, func, ...)
+  if not T.dispatchQueue[event] then
+    T.dispatchQueue[event] = {}
+  end
+
+  if not T.dispatchQueue[event][T.ID] then
+    T.dispatchQueue[event][T.ID] = T:GetName()
+  end
+
+  if (type(func) == 'function') then
+    if type(T[event]) == 'function' then
+      local oldfunc = T[event]
+      T[event] = function(...)
+        oldfunc(...)
+        return func(...)
+      end
+    else
+      T[event] = func
+    end
+  end
+  --@debug@
+  print(cText('Adding listener for'), cKey(self:GetName()), cText('on'), cKey(event))--@end-debug@
+end
+
+--- Default settings base
+T.defaults = {
+  char = {},
+
+  lefttext = {
+    parent = 2, -- in an overlay frame
+    anchor = 'LEFT',
+    anchorTo = 'LEFT',
+    size = 18,
+    x = 5, y = 0,
+    justifyH = 'LEFT',
+    justifyV = 'MIDDLE',
+  },
+
+  righttext = {
+    parent = 2, -- in an overlay frame
+    anchor = 'RIGHT',
+    anchorTo = 'RIGHT',
+    x = -5,
+    y = 0,
+    size = 18,
+    justifyH = 'RIGHT',
+    justifyV = 'MIDDLE',
+  },
+
+  alpha = 1,
+  alpha_ooc = 1,
+  alpha_fade_in = 0.2,
+  alpha_fade_out = 0.2,
+  border_color = {0,0,0,1},
+
+  background_color = {0,.0,0,1},
+  background_blend = 'BLEND',
+
+  foreground_texture = [[Interface\Addons\Turok\Media\statusbar\Minimalist.tga]],
+  foreground_color = {0,.475,.95,1},
+  foreground_blend = 'BLEND',
+
+  foreground_inset = 0,
+  padding = 2,
+  spacing = 0,
+  fill_direction = 'RIGHT',
+
+  anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
+  x = 0, y = 0,
+  inset = -3,
+  width = 250,
+  height = 100,
+  strata = 'LOW',
+  font = "Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf",
+  size = 14,
+  text_color = {1, 1, 1, 1},
+  justifyH = 'LEFT',
+  outline = 'OUTLINE',
+  combatFade = true,
+
+  battle_noise_start = [[Interface\Addons\Turok\Media\sound\Low_Beep-Public_D-136_hifi.mp3]],
+  battle_noise_end = [[Interface\Addons\Turok\Media\sound\Electro_-S_Bainbr-7955_hifi.mp3]],
+}
+
+
+T.events = {
+  'PLAY_MOVIE',
+  'PLAYER_TARGET_CHANGED',
+  'PLAYER_FOCUS_CHANGED',
+  'PLAYER_EQUIPMENT_CHANGED',
+  'PLAYER_REGEN_DISABLED',
+  'PLAYER_REGEN_ENABLED',
+  'SPELL_UDPATE_COOLDOWN',
+  'SPELL_UPDATE_USABLE',
+  'UNIT_AURA',
+  'UNIT_PET',
+  'UNIT_POWER_FREQUENT',
+  'UNIT_SPELL_HASTE',
+  'UNIT_SPELLCAST_SENT',
+  'UNIT_SPELLCAST_START',
+  'UNIT_SPELLCAST_DELAYED',
+  'UNIT_SPELLCAST_STOP',
+  'UNIT_SPELLCAST_CHANNEL_START',
+  'UNIT_SPELLCAST_CHANNEL_UPDATE',
+  'UNIT_SPELLCAST_CHANNEL_STOP',
+  'UNIT_SPELLCAST_FAILED',
+  'UNIT_SPELLCAST_INTERRUPTED',
+  'UNIT_SPELLCAST_INTERRUPTIBLE',
+  'UNIT_SPELLCAST_SUCCEEDED',
+  'UNIT_SPELLCAST_UNINTERRUPTIBLE',
+}
+T.previousSpec = {}
+T.talents = { {}, {}}
+T.changedTalents = {{}, {} }
+T.talentInfo = {}
+T.auras = {}
+T.spells = {}
+T.spellevent = {}
+T.channeling = {}
+T.casting = {}
+T.sent = {}
+T.prototype = {}
+T.spellBook = {}
+T.equipped = {}
+
+--- player-restricted unit info and text representations
+T.player = {}
+T.playertext = {}
+T.pet = {}
+
+--- holds non-restricted unit information
+T.unit = {
+  player = {},
+  target = {},
+  focus = {},
+  pet = {},
+}
+
+-- index of frames with conditional visual properties
+T.control_regions = {}
+
+-- index of frames generated by the lua
+T.frames = {}
+
+-- units
+T.units = {}
+T.unitsBySlot = {}
+setmetatable(T.unitsBySlot, {__mode="v"})
+
+
+
+T.TrueVal = function (self,k)
+  return rawget(self,k)
+end
+--- Sets an index hierarchy for db vars and propagates config dialog info
+T.LinkTable = function (over, under, pname, cname)
+  local mt = {
+    __index = function(t,k)
+      if type(over[k]) ~= nil then
+        --t[k] = over[k]
+        --@debug@
+        --print('up-referencing '.. STACK_COLOR2 .. pname ..'|r.'.. STACK_COLOR3.. tostring(k)..'|r -> '.. STACK_COLOR3.. pname ..'.' .. cname .. '|r.'..STACK_COLORN.. tostring(k) ..'|r', over[k])--@end-debug@
+      end
+      return over[k]
+    end,
+    __newindex = function (t, k, v)
+      rawset(t,k,v)
+      if type(v) == 'table' then
+        --@debug@
+        --print('parenting '.. STACK_COLOR2.. pname ..'|r to created table '.. STACK_COLOR3.. cname ..'|r')--@end-debug@
+        T.LinkTable(under, v, pname, k)
+      end
+    end
+  }
+  --under.TrueVal = T.TrueVal
+  setmetatable(under, mt)
+  for k, v in  pairs(under) do
+    if type(v) == 'table' then
+      --@debug@
+      --print('linking '..STACK_COLOR1.. pname ..'|r to '..STACK_COLOR2.. cname ..'|r')--@end-debug@
+      T.LinkTable(under, v, pname ..'.'.. cname, k)
+    end
+  end
+end
+
+
+--- Merges values of table B into table A, and copies over nested values of tables matching the keywords list
+local masked = {name = true, virtual = true}
+T.Config_Push = function(cvars, push , root, rkey)
+  local results = {}
+  root = root or cvars
+  for k, v in pairs(push) do
+    if not masked[k] then
+      if type(v) == 'table' and v ~= root then
+        cvars[k] = {}
+        T.Config_Push(cvars[k], v, root)
+      else
+        print('  |cFFFF0088B|r  ', tostring(rkey)..'.'..cKey(k))
+        cvars[k] = v
+        tinsert(results, '  |cFFFF0088B|r  '.. tostring(rkey)..'.'..cKey(k))
+      end
+    end
+  end
+  return cvars, results
+end
+T.Config_Merge = function(cvars, merge, root, rkey)
+  local diff = {}
+  root = root or cvars
+  rkey = rkey or '0'
+  for k, v in pairs(merge) do
+    if masked[k] or cvars[k] then
+      tinsert(diff, '  |cFF00FF00A|r  '.. tostring(rkey)..'.'..cWord(k))
+      print('  |cFF00FF00A|r  ', tostring(rkey)..'.'..cText(k), '=', cvars[k])
+      --cvars[k] = cvars[k]
+    else
+      if type(v) == 'table' then
+        if type(cvars[k]) == 'nil' then
+          cvars[k] = {}
+        end
+        if type(cvars[k]) == 'table' then
+          print('  |cFFFFFF00A+B|r  '.. tostring(rkey)..'.'..cWord(k))
+          cvars[k] = T.Config_Merge(cvars[k], v, root, tostring(rkey)..'.'..tostring(k))
+        end
+      elseif cvars[k] == nil then
+        tinsert(diff, '  |cFFFF0088B|r  '.. tostring(rkey)..'.'..cKey(k))
+        print('  |cFFFF0088B|r  ', tostring(rkey)..'.'..cKey(k))
+        cvars[k] = v
+      else
+        tinsert(diff, '  |cFF00FF00A|r  '.. tostring(rkey)..'.'..cWord(k))
+        print('  |cFF00FF00A|r  ', tostring(rkey)..'.'..cWord(k))
+        --cvars[k] = cvars[k]
+      end
+    end
+  end
+  return cvars, diff
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Layout/Dialog.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,186 @@
+--- Dialog Generator
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/1/2016 3:37 PM
+--- Framescript template for generating intelligible dialog panels
+--[[
+-- Frame << TkListFrame
+-- {
+--    Frame <rows> << TkListItem                <--  Any number of list item frames with parentArray="rows"
+--    {
+          :GetRow(row, values, id, offset)      <--  Populates the uiobjects that make up the given row, with data from values[id], on display row offset
+          <opts>                                <--  Any layered region of child frame with parentArray=opts"
+      }
+      Button <buttons>                      <--  Any frame with parentArray="buttons"
+
+      .Click (button, list)                 <--  Defines results of using a control button
+      .Check (button, row, list)            <--  Defines results of operating a row widget
+      .Wheel (list, delta)                  <--  Defines response to mousewheel action over the list panel
+   }
+
+   Globals:
+   TkList_Init ( object, values, offset, numRows )
+    Takes the return from CreateFrame and a table of values and establishes a scrollable list interface.
+
+   TkPanel_Init ( object )
+    TkList_Init without the scrollable list operations. Lines up control buttons and nothing else.
+--]]
+local TK_LIST_SPACING = 1
+local TK_LIST_PADDING = 3
+local TK_LIST_HEADING_SIZE = 26
+local TK_LIST_ITEM_HEIGHT = 24
+local TK_LIST_DISPLAY_ITEMS = 10
+
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Xui', ...)
+  end
+end
+-- GLOBALS: TkList_SetView, TkList_Init, Turok
+local type, error, pairs, ipairs, tonumber, max, CreateFrame = type, error, pairs, ipairs, tonumber, math.max, CreateFrame
+local checkval = function(valtype, t, fallback)
+  return (type(t) == valtype) and t or (fallback or error('Expected table reference, got '..type(t)))
+end
+local tableval = function(t, fallback)
+  return checkval('table', t, fallback or {})
+end
+local funcval = function(f, fallback)
+  return checkval('function', f, fallback or function() end)
+end
+
+--- Orders list row
+-- Does an ipairs iteration over self.opts and distributes the referenced objects along the horizontal axis of 'self'
+-- Note that members are only aligned horizontally; no column alignment is done. Widths can be set by datafunc to accomplish that.
+local function TkListItem_Init (listrow)
+  if not listrow.opts then
+    print('no options to enumerate')
+    return
+  end
+  local rwidth = TK_LIST_PADDING*2
+  for i, z in pairs(listrow.opts) do
+    if i > 1 then
+      z:SetPoint('LEFT', listrow.opts[i-1], 'RIGHT', TK_LIST_SPACING, 0)
+    else
+      z:SetPoint('LEFT', listrow, 'LEFT', TK_LIST_PADDING, 0)
+    end
+    rwidth = rwidth + z:GetWidth()+TK_LIST_SPACING
+    z:Show()
+  end
+  listrow:SetSize(rwidth-TK_LIST_PADDING*2,TK_LIST_ITEM_HEIGHT)
+  listrow:Show()
+  print(rwidth)
+  return rwidth
+end
+
+
+---
+local function TkControls_Init(self)
+  local buttonsize = (self._dwidth - (#self.buttons -1) * TK_LIST_SPACING - TK_LIST_PADDING*2) / #self.buttons
+  for n, b in ipairs(self.buttons) do
+    --print('Spirit', buttonsize)
+    b:SetWidth(buttonsize)
+    b:SetPoint('TOPLEFT', self, 'BOTTOMLEFT', buttonsize* (n-1) + TK_LIST_SPACING * (n-1) + TK_LIST_PADDING, -TK_LIST_PADDING)
+  end
+  self.controls:SetHeight(self.buttons[1]:GetHeight()+ TK_LIST_PADDING*2)
+end
+
+--- Populates the list frame with items
+function TkList_SetView(self, start_num, num_rows)
+  print(self, start_num, num_rows)
+  if not start_num then
+    start_num = self.offset
+  end
+
+  if not num_rows then
+    num_rows = self.num_rows
+  end
+
+  if start_num > #self.info then
+    start_num = #self.info - #self.info % num_rows
+  elseif start_num < 1 then
+    start_num = 1
+  end
+
+  print('  ', self:GetName(), start_num, num_rows, #self.info)
+  self.offset = start_num
+
+  for draw_num = 1, num_rows do
+    local actual_row = draw_num + start_num - 1
+    if not self.rows[draw_num] then
+      self.rows[draw_num] = CreateFrame('Frame', self:GetName()..'_Option_'..draw_num, self, 'TurokListItem')
+      self.rows[draw_num]:SetHeight(TK_LIST_ITEM_HEIGHT)
+      self.rows[draw_num].row_num = draw_num
+    end
+    self.rows[draw_num].actual_row = actual_row
+    local row = self.rows[draw_num]
+    if self.info[actual_row] then
+      print( actual_row, draw_num)
+      self.GetRow(row, self.info, actual_row, draw_num)
+      row:Show()
+    else
+      print('|cFF888888'..actual_row, draw_num)
+      row:Hide()
+    end
+    row:SetParent(self)
+  end
+end
+
+--- Prime TkListFrame template-spawn for use
+-- @param self frame object to list-ify
+-- @param info array of list item values
+-- @param datafunc function that takes 1) frame object 2) info table 3) index number 4) row number
+-- @param offset starting offset for initial view
+function TkList_Init (self, info, offset, num_rows)
+  -- error checking
+  --[[if type(info) ~= 'table' then
+    error('arg #2 info must be an associative array')
+  end
+  if type(datafunc) ~= 'function' then
+    error('arg #3 needs a funcref or method name from the frame object')
+  end
+  --]]
+  self.GetRow = funcval(self.GetRow, function() end)
+  self.info = tableval(info, {})
+  for i, v in ipairs(info) do
+    print('Main', i, '=', v.spellName, v.spellTab)
+    self.max_row = i
+  end
+  print('Main', 'last row #', self.max_row)
+
+
+  -- obtain data contents
+  self._dwidth = 0
+  self.rows = {}
+  self.offset = offset and offset or 1
+  self.num_rows = (tonumber(num_rows) ~= nil) and num_rows or TK_LIST_DISPLAY_ITEMS
+  self.name:SetHeight(TK_LIST_HEADING_SIZE)
+  TkList_SetView(self, offset)
+
+  -- sort out the proper width
+  local rwidth = 1
+  local  k = 0
+  for n, item in ipairs(self.rows) do
+    print(item:GetName())
+    if item:IsShown() then
+      if n > 1 then
+        item:SetPoint('TOPLEFT', self.rows[n-1], 'BOTTOMLEFT', 0, - TK_LIST_SPACING)
+      else
+        item:SetPoint('TOPLEFT', self, 'TOPLEFT', TK_LIST_PADDING, -(TK_LIST_PADDING+TK_LIST_HEADING_SIZE+TK_LIST_SPACING))
+      end
+      k = k+1
+      self._dwidth = max(self._dwidth, TkListItem_Init(item))
+    end
+  end
+
+  TkControls_Init(self)
+
+  self:SetSize(self._dwidth , (k*TK_LIST_ITEM_HEIGHT)+(TK_LIST_PADDING*2)+(k*TK_LIST_SPACING)+TK_LIST_HEADING_SIZE)
+end
+
+--- Set up for a non-list-style panel, no iterative controls
+function TkPanel_Init(self)
+  self._dwidth = self:GetWidth()
+  TkControls_Init(self)
+  --self:SetSize(self._dwidth , (k*TK_LIST_ITEM_HEIGHT)+(TK_LIST_PADDING*2)+(k*TK_LIST_SPACING)+TK_LIST_HEADING_SIZE)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Layout/Dialog.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,462 @@
+<Ui>
+  <!-- Visual element structures for configuration interfaces  -->
+  <Script file="Dialog.lua" />
+  <Font name="TkDialogFieldFont" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf" outline="NORMAL">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="13"/>
+    </FontHeight>
+  </Font>
+
+
+  <Button name="TurokButton" virtual="true" parentArray="buttons" enableMouse="true">
+    <Scripts>
+      <OnShow>
+        self:RegisterForClicks("LeftButtonUp")
+        self.bname:SetText(self:GetName():match("_(%a+)$"))
+      </OnShow>
+      <OnClick>
+        self:GetParent().Click(self, self:GetParent())
+      </OnClick>
+    </Scripts>
+    <NormalTexture setAllPoints="true">
+      <Color r="0" g="0" b="0" a="1" />
+    </NormalTexture>
+    <PushedTexture setAllPoints="true" alphaMode="ADD">
+      <Color r="1" g="0.5" b="0" a="1" />
+    </PushedTexture>
+    <HighlightTexture setAllPoints="true" alphaMode="ADD">
+      <Color r="0.5" g="0.1" b=".4" a="1" />
+    </HighlightTexture>
+    <DisabledTexture setAllPoints="true">
+      <Color r="0.4" g="0.4" b="0.4" a="1" />
+    </DisabledTexture>
+    <Size x="100" y="20" />
+    <Layers>
+      <Layer level="BACKDROP">
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString inherits="TurokFont" parentKey="bname" justifyH="CENTER" justifyV="MIDDLE" setAllPoints="true" text="Foo" />
+      </Layer>
+      <Layer level="HIGHLIGHT">
+      </Layer>
+    </Layers>
+  </Button>
+
+  <Frame name="TurokDialogMenu" parent="UIParent" enableMouse="true" hidden="true">
+    <Size x="200" y="200" />
+    <Anchors>
+      <Anchor point="TOPLEFT" />
+    </Anchors>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture setAllPoints="true">
+          <Color r="0" g="0" b="0" a="0.2" />
+        </Texture>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <CheckButton name="TurokCheckButton" virtual="true" enableMouse="true">
+    <Scripts>
+      <OnClick>
+        if self:GetParent().Check then
+          self:GetParent().Check(self, self:GetParent())
+        else
+          self:GetParent():GetParent().Check(self, self:GetParent(), self:GetParent():GetParent())
+        end
+      </OnClick>
+    </Scripts>
+    <NormalTexture>
+      <Color r="0.3" g="0.05" b="0.6" a="1" />
+      <Size x="16" y="16" />
+      <Anchors>
+        <Anchor point="TOPLEFT" x="2" y="-2" />
+      </Anchors>
+    </NormalTexture>
+    <PushedTexture>
+      <Color r="0" g="0" b="0" a="1" />
+      <Size x="16" y="16" />
+      <Anchors>
+        <Anchor point="TOPLEFT" x="2" y="-2" />
+      </Anchors>
+    </PushedTexture>
+    <HighlightTexture>
+      <Color r="1" g="0" b="0" a="0.1" />
+      <Size x="16" y="16" />
+      <Anchors>
+        <Anchor point="TOPLEFT" x="2" y="-2" />
+      </Anchors>
+    </HighlightTexture>
+    <CheckedTexture>
+      <Color r="1" g="1" b="0.5" a="1" />
+      <Size x="16" y="16" />
+      <Anchors>
+        <Anchor point="TOPLEFT" x="2" y="-2" />
+      </Anchors>
+    </CheckedTexture>
+
+    <DisabledCheckedTexture>
+      <Color r="0.7" g=".7" b=".7" a="1" />
+      <Size x="18" y="18" />
+      <Anchor point="TOPLEFT" />
+    </DisabledCheckedTexture>
+  </CheckButton>
+
+  <CheckButton name="TurokCheckButtonInline" inherits="TurokCheckButton" virtual="true" enableMouse="true">
+    <Size x="20" y="20" />
+    <Layers>
+      <Layer level="BORDER">
+        <Texture name="$parentBorder">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="1" y="-1" />
+            <Anchor point="BOTTOMRIGHT" x="-1" y="1" />
+          </Anchors>
+          <Size x="18" y="18" />
+          <Color r="1" g="1" b="1" a="1" />
+          <Gradient>
+            <MinColor r="1" g="0" b=".5" a="1" />
+            <MaxColor r="0.5" g="0.9" b="1" a="1" />
+          </Gradient>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentDescText" inherits="TurokFontDetail" justifyH="LEFT" parentKey="description" text="Inline CheckButton">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="4" y="-3" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </CheckButton>
+
+  <!-- CheckButton designed to collapse on top of an EditBox -->
+  <CheckButton name="TurokCheckButtonOverlay" inherits="TurokCheckButton" virtual="true" enableMouse="true" frameStrata="HIGH">
+    <Size x="20" y="48" />
+    <Layers>
+      <Layer level="BORDER">
+        <Texture name="$parentBorder">
+          <Size x="18" y="18" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="1" y="-1" />
+          </Anchors>
+          <Color r="1" g="1" b="1" a="1" />
+          <Gradient>
+            <MinColor r="1" g="0" b=".5" a="1" />
+            <MaxColor r="0.5" g="0.9" b="1" a="1" />
+          </Gradient>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentDescText" inherits="TurokFontDetail" justifyH="LEFT" parentKey="description" text="Overlay CheckButton">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="4" y="-3" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </CheckButton>
+
+  <Frame name="TurokDialogFrame" parent="UIParent" enableMouse="true" hidden="true" movable="true" virtual="true" toplevel="true" mouseWheel="true">
+    <Scripts>
+      <OnShow>
+        for i, g in pairs({self:GetChildren()}) do
+          g:Show()
+        end
+        self:RegisterForDrag("LeftButton")
+      </OnShow>
+      <OnDragStart>
+        self:StartMoving()
+      </OnDragStart>
+      <OnDragStop>
+        self:StopMovingOrSizing()
+      </OnDragStop>
+      <OnMouseWheel>
+        self:Wheel(delta)
+      </OnMouseWheel>
+    </Scripts>
+    <Anchors>
+      <Anchor point="CENTER" />
+    </Anchors>
+    <Size x="300" y="200" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture setAllPoints="true" blendMode="ADD">
+          <Color r="1" b="1" g="1" a="1" />
+          <Gradient orientation="HORIZONTAL">
+            <MinColor r="1" g="0" b="0.5" a="0.5" />
+            <MaxColor r="1" g=".5" b="0" a="0.5" />
+          </Gradient>
+        </Texture>
+        <Texture blendMode="ADD" parentKey="controls">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" />
+            <Anchor point="TOPRIGHT" relativePoint="BOTTOMRIGHT" />
+          </Anchors>
+          <Color r="1" b="1" g="1" a="1" />
+          <Gradient orientation="HORIZONTAL">
+            <MinColor r=".4" g=".4" b="1" a="1" />
+            <MaxColor r="1" g="0" b=".5" a="1" />
+          </Gradient>
+        </Texture>
+      </Layer>
+      <Layer level="BORDER">
+        <Texture blendMode="BLEND">
+          <Color r="0" g="0" b="0" a="1" />
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+            <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" x="0" y="-25" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentTitle" parentKey="name" inherits="TurokFont" justifyV="TOP" justifyH="LEFT" text="Set Title!">
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="5" y="-5" />
+          </Anchors>
+          <Size x="200" y="40" />
+        </FontString>
+        <FontString name="$parentTitle" parentKey="pagenum" inherits="TurokFont" justifyV="TOP" justifyH="RIGHT">
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="TOPRIGHT" x="-25" y="-5" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+    <Frames>
+
+      <Button name="$parentButton_Close" inherits="UIPanelCloseButton">
+        <Size x="30" y="30" />
+        <Anchors>
+          <Anchor point="TOPRIGHT" x="3" y="3" />
+        </Anchors>
+      </Button>
+    </Frames>
+  </Frame>
+
+  <!-- LIST FRAME OVERHEAD -->
+  <Frame name="TurokListFrame" inherits="TurokDialogFrame" virtual="true">
+    <Frames>
+      <Button name="$parentButton_Prev" inherits="TurokButton" />
+      <Button name="$parentButton_Add"  inherits="TurokButton" />
+      <Button name="$parentButton_Next"  inherits="TurokButton" />
+    </Frames>
+  </Frame>
+
+  <!-- LIST FRAME ROW -->
+  <Frame name="TurokListItem" parent="UIParent" parentArray="tabs" enableMouse="true" hidden="true" virtual="true">
+    <Scripts>
+      <OnEnter>
+        GameTooltip:SetOwner(self, "ANCHOR_LEFT");
+        if self.spellID then
+          GameTooltip:SetSpellByID(self.spellID);
+        else
+          GameTooltip:SetText(self.desc)
+        end
+        GameTooltip:Show();
+      </OnEnter>
+      <OnLeave function="GameTooltip_Hide"/>
+    </Scripts>
+    <Size x="300" y="30" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="background" name="$parentTab" blendMode="BLEND">
+          <Size x="50" y="21" />
+          <Color r="0" g="0" b="0" a=".3" />
+          <Anchors>
+            <Anchor point="LEFT" relativePoint="RIGHT" relativeTo="$parentTab" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="HIGHLIGHT">
+        <Texture name="$parent_Highlight" parentKey="highlight" selAllPoints="true" blendMode="ADD">
+          <Color r="1" g="1" b="1" a=".25" />
+        </Texture>
+      </Layer>
+
+      <Layer level="OVERLAY">
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="TkWidgetScripts" virtual="true">
+    <Scripts>
+      <OnValueChanged>
+        if self.value then
+          self.value:SetText(tostring(math.floor(value)))
+          self.value:SetPoint('LEFT', self:GetThumbTexture(), 'LEFT', 1, 0)
+        end
+      </OnValueChanged>
+    </Scripts>
+  </Frame>
+
+  <Slider name="TkSlider" orientation="HORIZONTAL" enableMouse="true"
+          parentArray="_scroller"
+          minValue="0" maxValue="800" valueStep="4" stepsPerPage="3" defaultValue="400" inherits="TkWidgetScripts" virtual="true">
+    <HitRectInsets left="0" right="0" bottom="0" top="0" />
+    <Anchors><Anchor point="CENTER" /></Anchors>
+    <Size x="350" y="30" />
+    <ThumbTexture parentKey="thumb" name="$parentThumb">
+      <Color r="1" g=".45" b="0" a="1" />
+      <Size x="30" y="30" />
+      <Anchors>
+        <Anchor point="BOTTOM" />
+      </Anchors>
+    </ThumbTexture>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="background">
+          <Color r="0" g="0" b="0" a="0.5" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="0" y="-16" />
+            <Anchor point="BOTTOMRIGHT" x="0" y="0" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString parentKey="value" inherits="TurokFontDetail" />
+        <FontString parentkey="name" inherits="TurokFont">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="TOPLEFT" />
+          </Anchors>
+        </FontString>
+      </Layer>
+      <Layer level="HIGHLIGHT">
+
+        <Texture alphaMode="MOD">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="0" y="-16" />
+            <Anchor point="BOTTOMRIGHT" x="0" y="0" />
+          </Anchors>
+          <Color r="0.5" b="0.5" g="0.5" a="0.25" />
+        </Texture>
+      </Layer>
+    </Layers>
+  </Slider>
+
+
+  <EditBox name="TkEditBox" font="TurokFont" inherits="TkWidgetScripts" virtual="true"
+           historyLines="3"
+           autoFocus="false"
+           parentArray="_inputs">
+    <Backdrop bgFile="Interface\Addons\Turok\Media\border\BG-Solid.blp" edgeFile="Interface\Addons\Turok\Media\border\BigBorder-Solid.blp" tile="true">
+      <EdgeSize val="12"/>
+      <TileSize val="12"/>
+      <BorderColor r="0" g="0" b="0" a="1" />
+      <Color r="1" g="1" b="1" a="1" />
+      <BackgroundInsets>
+        <AbsInset left="3" right="3" top="3" bottom="3"/>
+      </BackgroundInsets>
+    </Backdrop>
+    <Scripts>
+      <OnEscapePressed>
+        self:ClearFocus()
+      </OnEscapePressed>
+      <OnEnterPressed>
+        self:ClearFocus()
+      </OnEnterPressed>
+      <OnEditFocusGained>
+        print(self:GetName(),'gained focus')
+        self.__bg = {self.background:GetTexture()}
+        self.background:SetTexture(0,0,0,1)
+      </OnEditFocusGained>
+      <OnEditFocusLost>
+        print(self:GetName(),'lost focus')
+        self:GetParent():GetParent().EditBox(self)
+        self.background:SetTexture(unpack(self.__bg))
+        self.__bg = nil
+      </OnEditFocusLost>
+    </Scripts>
+    <Size x="350" y="48" />
+    <TextInsets>
+      <AbsInset left="4" top="22" right="4" bottom="0" />
+    </TextInsets>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="border" alphaMode="MOD">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" x="3" y="3" />
+            <Anchor point="TOPRIGHT" x="-3" y="-3" />
+          </Anchors>
+          <Color r="1" g="1" b="1" a="1" />
+          <Gradient orientation="HORIZONTAL">
+            <MinColor r="0.4" g="0" b="0.2" a="1" />
+            <MaxColor r="1" g="0.35" b="0.15" a="1" />
+          </Gradient>
+        </Texture>
+      </Layer>
+      <Layer level="BORDER">
+        <Texture parentKey="background">
+          <Color r="0" g="0" b="0" a="0.5" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="3" y="-22" />
+            <Anchor point="BOTTOMRIGHT" x="-3" y="3" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString parentkey="description" inherits="TkDialogFieldFont" justifyV="TOP" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="TOPLEFT" x="5" y="-6" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </EditBox>
+
+
+  <Button name="TurokDDButton" inherits="TurokButton" parent="UIParent" enableMouse="true" virtual="true">
+    <NormalTexture setAllPoints="true">
+      <Color r=".35" g="0" b=".7" a="1" />
+    </NormalTexture>
+  </Button>
+
+  <Frame name="TurokDialogRow" enableMouse="true" hidden="true" movable="true" virtual="true" toplevel="true" mouseWheel="true">
+    <Size x="200" y="42" />
+  </Frame>
+
+  <!-- TIMER EDIT FRAME -->
+  <Frame name="TkTimerConfig" parent="UIParent" hidden="true" inherits="TurokDialogFrame">
+    <Scripts>
+      <OnLoad>
+        self.Wheel = function(self, delta) print(self:GetName(), 'wheel stuff')
+        if self._scroller then
+          for i, region in ipairs(self._scroller) do
+            if region:IsMouseOver() then
+              print(self:GetName(),'hit #', i, region:GetName())
+              region:SetValue(region:GetValue()+delta)
+            end
+          end
+        else
+          print(self:GetName(),'no scrollwheel regions')
+        end
+        end
+
+        print('uhmmm', type(self))
+        LibStub("LibFog-1.0").SetAnimationGroup(self)
+        -- Turok stuff
+      </OnLoad>
+      <OnShow>
+        TkPanel_Init(self)
+        self:RegisterForDrag("LeftButton")
+        self.name:SetText(self.timerName)
+        self.pagenum:SetText(self.timerID)
+        self:SetAlpha(0)
+        self:Fade(0.2, 1)
+      </OnShow>
+    </Scripts>
+    <Size x="400" y="300" />
+    <Anchors>
+      <Anchor point="CENTER" />
+    </Anchors>
+    <Frames>
+
+
+      <Button name="$parentButton_Spells" inherits="TurokButton" />
+      <Button name="$parentButton_Move" inherits="TurokButton" />
+      <Button name="$parentButton_Delete" inherits="TurokButton" />
+    </Frames>
+  </Frame>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Layout/Layout.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,725 @@
+--- LibFog-1.0
+-- In-house solution to processing frame configuration data.
+--[[
+--
+ Config Table fields:
+
+ background .. _color, _texture, _blend       properties of the texture referenced by frame.background
+ foregound  .. " " "                          properties of the texture referenced by frame.foreground
+ backdrop                                     arguments for SetBackdrop
+ border_color                                 sets the border color on an existing frame backdrop
+ alpha, alpha_ooc                             uiobject alpha for in and out of combat
+ combat_fade                                  true to change alpha levels based on combat
+ fade_time, fade_in, fade_out                 fade_time = fade_in or fade_out
+
+ anchor, parent, anchorTo, x, y               args for UIObject:SetPoint()
+ height, width                                args for Region:SetSize()
+ strata, layer, level                         override the strata, draw layer, and draw level where they apply
+
+ padding                                      space between frame and background edge
+ foreground_inset                             space between background and foreground edge
+ spacing                                      space between segmented foreground elements (e.g. combo points)
+
+ font, size, outline                          args for FontString:SetFont() respectively
+ justifyV, justifyH                           LEFT/CENTER/RIGHT, TOP/MIDDLE/BOTTOM
+
+ SetFrameLayout():
+ Applies:
+  anchor, parent, anchorTo, x, y
+  width, height,
+  padding, spacing, foreground_inset
+  backdrop, border_color, background_color
+  alpha, alpha_ooc
+
+ SetStatusTextures():
+ Applies:
+  foreground_texture, foreground_color, foreground_blend
+  background_texture, background_color, background_blend
+
+ Defines:
+  fill_direction, fill_width, fill_height, fill_inset
+  :TranslateX(progress)
+  :TranslateY(progress)
+  :SetFillPoints()
+
+ SetFontLayout():
+ Applies:
+  anchor, parent, anchorTo, x, y
+  width, height
+  font, size, outline
+  justifyH, justifyV
+
+ -
+ ]]
+local MAJOR, MINOR = "LibFog-1.0", 1
+local F, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not F then return end
+
+local _G, UIParent, type, unpack, pairs, ipairs, min, abs, tostring, print = _G, UIParent, type, unpack, pairs, ipairs, math.min, math.abs, tostring, print
+local InCombatLockdown, GetTime, PlaySoundKitID, CreateFrame = InCombatLockdown, GetTime, PlaySoundKitID, CreateFrame
+local tinsert, wipe, concat = table.insert, table.wipe, table.concat
+local FADE_OUT_TIME, FADE_IN_TIME = 1, 0.4
+
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local ADDON, scriptargs = ...
+scriptargs.LibFog = F
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Layout', ...)
+  end
+end
+--- debug highlighters
+local namef  = function(s) return '|cFFFFFF00' ..tostring(s).. '|r' end    -- uiobject targeted function
+local namet  = function(s) return '|cFFFF4400 '..tostring(s).. '|r' end    --   region targeted function
+local valuef = function(s) return '|cFF88FF88'..(s ~= nil and ('"'..tostring(s)..'"') or ('nil'))..'|r' end    -- uiobject name
+local valuet = function(s) return '|cFFAACCAA'..(s ~= nil and ('"'..tostring(s)..'"') or ('nil'))..'|r' end    --   string value
+local valuen = function(s) return '|cFFAADD77' ..(s ~= nil and ('"'..tostring(s)..'"') or ('nil')).. '|r' end    --   string enumeration
+local valued = function(s) return '|cFFFFFFFF' ..(s ~= nil and ('"'..tostring(s)..'"') or ('nil')).. '|r' end    --   number
+--@end-debug@
+local GetPrint = function(trace)
+  if trace then
+    return print
+  else
+    return function() end
+  end
+end
+
+--- Layout control metadata
+local lastdb      -- fallback if a table isn't found mid-operation
+local ui_meta = {
+  beepkit = 15263, -- something to play when debugging
+}
+local ui_embeds = {
+  ['Frame'] = 'SetFrameLayout',
+  ['Texture'] = 'SetTextureLayout',
+  ['Font'] = 'SetFontLayout',
+}
+
+--- Resolve a parent object from varying depths
+-- @param self target object
+-- @parent string/number name of global frame or number of GetParent() hops to make
+-- @childKey index key to retrieve from the resolved parent object
+local function ParentValue(self, parent, childKey)
+  local print = GetPrint(self.trace)
+
+  local relativeTo = parent
+  if type(parent) == 'number' then
+    print(cWord('* Parent Trace:'), cNum(parent), cKey(self:GetName()))
+    relativeTo = self:GetParent()
+    for i = 2, parent do
+      if relativeTo.GetParent and relativeTo:GetParent() ~= UIParent then
+        --@debug@
+        print('  ', 'parent =', relativeTo:GetName())
+        --@end-debug
+        local next = relativeTo:GetParent()
+        relativeTo = next
+      else
+        --@debug@
+        print('  ',cWord(self:GetName()), 'parent =', cWord(parent))
+        --@debug-end@
+      end
+    end
+  end
+
+  -- This allows us to call for an immediate sub-frame and still get a usable value if it's not there
+  if childKey and relativeTo[childKey] then
+    return relativeTo[childKey]
+  end
+  return relativeTo
+end
+
+--- Animations
+F.animate_regions = {}
+setmetatable(F.animate_regions, {
+  __newindex = function (t,k,v)
+    rawset(t,k,v)
+    v.animationID = k
+    F.SetAnimationGroup(v)
+  end
+})
+
+--- Constructs the generic status frame
+function F:SetFrameLayout(c)
+  local print = GetPrint(c.trace)
+  self.trace = c.trace or self.trace
+
+  self.db = c
+  self.labels = {}
+
+  self.name = self:GetName()
+  self.combat = InCombatLockdown()
+  self:SetPoint(c.anchor, c.parent, c.anchorTo, c.x, c.y)
+  self:SetSize(c.width, c.height)
+  self:SetFrameStrata(c.strata)
+  self.throttle_rate = 0.010
+  self.throttle_point = GetTime()
+
+  if c.backdrop then
+    c.backdrop.edgeSize = c.padding*4
+    self:SetBackdrop(c.backdrop)
+  end
+  if c.background_color and c.border_color then
+    self:SetBackdropColor(unpack(c.background_color))
+    self:SetBackdropBorderColor(unpack(c.border_color))
+  end
+
+  --@debug@
+  print(namef('SetFrameLayout'),'(', valuet(self:GetName()),')')
+  print(cText('  dimensions:'), cNum(c.width), 'by', cNum(c.height), '::', c.x, c.y,  c.anchor, c.anchorTo, c.parent)
+  --@end-debug@
+
+  self.width = c.width
+  self.height = c.height
+  self.foreground_inset = c.foreground_inset
+  self.padding = c.padding
+  self.spacing = c.spacing
+  self.alpha = c.alpha
+
+  self.combatFade = c.combatFade
+  self.alpha_fade_in = c.alpha_fade_in or c.alpha_fade
+  self.alpha_fade_out = c.alpha_fade_out or c.alpha_fade
+  print('  fade on combat =', self.combatFade)
+  for _, type in ipairs({'alpha', 'alpha_ooc'}) do
+    self[type] = c[type]
+    _G.print('DB', self:GetName(), type, self[type])
+    for _, subtype in ipairs({'_passive', '_active'}) do
+      self[type..subtype] = c[type..subtype] or self[type]
+      _G.print('DB', self:GetName(), type..subtype, self[type..subtype])
+      for _, subsubtype in ipairs({'_empty', '_half', '_full'}) do
+        self[type..subtype..subsubtype] = c[type..subtype..subsubtype] or self[type..subtype]
+        _G.print('DB', self:GetName(), type..subtype..subsubtype, self[type..subtype..subsubtype])
+        self[type..subsubtype] = c[type..subsubtype] or self[type..subtype]
+        _G.print('DB', self:GetName(), type..subsubtype, self[type..subsubtype])
+      end
+    end
+  end
+  self.UpdateAlpha = F.UpdateAlpha
+  --if c.combatFade then
+  self.faderID = #F.animate_regions+1
+  F.animate_regions[#F.animate_regions+1] = self
+  print(cText('  animation target ID #'), cNum(self.faderID))
+  --end
+
+  self:UpdateAlpha(InCombatLockdown())
+
+  if self.lefttext then
+    print('  auto-lefttext')
+    F.SetFontLayout(self.lefttext, c.lefttext or c)
+  end
+  if self.righttext then
+    print('  auto-righttext')
+    F.SetFontLayout(self.righttext, c.righttext or c)
+  end
+end
+
+--- Seeds basic animations, else a template can be specified by the frame data
+function F:SetAnimationGroup(group)
+  local print = GetPrint(self.trace)
+
+  group = group or self.animationClass or nil
+  --@debug@
+  if not group then
+    print('  |cFFFF44AAseeding debug animations', self:GetName())
+    self.__flash = self:CreateAnimationGroup(self:GetName()..'Flasher')
+    self.__flash:SetToFinalAlpha(true)
+    local fade1 = self.__flash:CreateAnimation('Alpha')
+    fade1:SetChange(-1)
+    fade1:SetDuration(.6)
+    fade1:SetOrder(1)
+    local fade2 = self.__flash:CreateAnimation('Alpha')
+    fade2:SetChange(1)
+    fade2:SetDuration(.6)
+    fade2:SetOrder(2)
+    fade2:SetEndDelay(.6)
+    --@debug@
+    self.__flash.fade1 = fade1
+    self.__flash.fade2 = fade2
+    self.__flash:SetLooping('NONE')
+    self.__flash:SetScript('OnFinished', function()
+      print(self:GetName(), '[>>>] Done animating flash.')
+      self.flashing = nil
+    end)--@end-debug@
+    self.Flash = F.Flash
+
+    self.__fade = self:CreateAnimationGroup(self:GetName()..'Fader')
+    self.__fade:SetToFinalAlpha(true)
+    self.__fade:SetScript('OnFinished', function()
+      print(self:GetName(), '[>>>] Done animating fade.')
+      self.fading = nil
+      local a = self.__flash.fade1:GetToAlpha()
+      --self:SetAlpha(a)
+      if a == 0 and not self.__fade.noHide then
+        self:Hide()
+      end
+      if self.__fade.queuedFade then
+        print('[>>>]  ', cWord(self:GetName())..'.'..cKey('__fade:'), 'starting queued fade')
+        local fadeTo, fadeDuration, noHide = unpack(self.__fade.queuedFade)
+        self.__fade.queuedFade = nil
+        self:Fade(fadeTo, fadeDuration, noHide)
+      end
+    end)
+    self.Fade = F.Fade
+  end
+  --@end-debug@
+  if not self.animationID then
+    tinsert(F.animate_regions, self) -- uses rawset, so won't death loop
+    self.animationID = #F.animate_regions
+  end
+end
+
+function F:Flash(duration, peak, valley, holdUp, holdDown)
+  local print = GetPrint(self.trace)
+
+  local fl = self.__flash
+  print('[>>>]', self:GetName(), duration, peak, valley, holdUp, holdDown)
+  fl.fade1:SetFromAlpha(valley)
+  fl.fade1:SetToAlpha(peak)
+  fl.fade1:SetDuration(duration)
+  fl.fade1:SetEndDelay(holdUp)
+  fl.fade2:SetFromAlpha(peak)
+  fl.fade2:SetToAlpha(valley)
+  fl.fade2:SetDuration(duration)
+  fl.fade2:SetEndDelay(2)
+  self.flashing = true
+  fl:Play()
+end
+
+--- Fade to a value; fading to 0 will hide the frame
+function F:Fade(duration, fadeTo, noHide)
+
+  print('|cFFFF6600[>>>]', cText('Fade('), self:GetName(), cText(')'))
+  local fader = self.__fade
+  local fade1 = self.__flash.fade1
+  if fader:IsPlaying() then
+    self.__fade.queuedFade = {duration, fadeTo, noHide }
+    print('    ', cText('playing:'), cNum(fader:GetDuration())..'s', cNum(fade1:GetToAlpha()))
+    print('    ', cPink('queued {'),  cNum(duration)..'s', cNum(self:GetAlpha()), 'to', cNum(fadeTo), '}')
+    return
+  else
+    print('    ', cNum('starting {'), cNum(duration), cNum(self:GetAlpha()), 'to', cNum(fadeTo), '}')
+  end
+  fade1:SetParent(fader)
+  fade1:SetFromAlpha(self:GetAlpha())
+  fade1:SetToAlpha(fadeTo)
+  fade1:SetDuration(duration)
+  fade1:SetOrder(1)
+  self.__fade.noHide = noHide
+  self.fading = true
+  fader:Play()
+end
+
+function F:Beep()
+  if self.quiet then
+    return
+  end
+  PlaySoundKitID(F.beepkit) -- random anub'rhekan sounds
+end
+
+
+--- Defines the corners used in filling operations
+local directionBase = {
+  ['LEFT'] = 'TOPRIGHT',
+  ['RIGHT'] = 'TOPLEFT',
+  ['UP'] = 'BOTTOMLEFT',
+  ['DOWN'] = 'TOPLEFT'
+}
+local directionPeak ={
+  ['LEFT'] = 'BOTTOMLEFT',
+  ['RIGHT'] = 'BOTTOMRIGHT',
+  ['UP'] = 'TOPRIGHT',
+  ['DOWN'] = 'BOTTOMRIGHT',
+}
+local directionPeakTo = {
+  ['LEFT'] = 'BOTTOMRIGHT',
+  ['RIGHT'] = 'BOTTOMLEFT',
+  ['UP'] = 'BOTTOMRIGHT',
+  ['DOWN'] = 'TOPRIGHT'
+}
+local anchorInverse = {['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['TOP'] = 'BOTTOM', ['BOTTOM'] = 'TOP' }
+--- Directional coefficients for corner positions
+-- [1] = end X, [2] = end Y, [3] = base X, [4] = base Y
+local directionCoord = {
+  ['LEFT']   = {-1,  0, -1, -1},
+  ['RIGHT']  = { 1,  0, 1, -1},
+  ['UP']     = { 0, 1, 1, 1},
+  ['DOWN']   = { 0, -1, 1, -1},
+  ['CENTER'] = {.5, .5, 1, 1}
+}
+local paddingScale = {
+  ['UP'] = 1,
+  ['DOWN'] = -1,
+  ['LEFT'] = -1,
+  ['RIGHT'] = 1,
+}
+local embedScale = {
+  ['LEFT'] = {1, false},
+  ['RIGHT'] = {-1, false},
+  ['TOP'] = {false, -1},
+  ['BOTTOM'] = {false, 1}
+}
+
+--- Determines the correct relative X offset for a given progress ratio
+--- x = 0 + padding + spacing + i, where i = {embedded | icon_size +spacing}
+-- Factors padding and spacing parameters, including icon embedding dimensions.
+-- @param self frame on which the foreground/background textures and config data exist
+-- @param ratio progess from 0 to 1; 0 returns the starting corner coordinate, 1 returns end corner
+-- @param scale overrides the pixel fill length, with starting corner used as scaling origin
+local TranslateX    = function(self, ratio, scale)
+  if not scale then
+    scale = self.fill_width
+  end
+  if self.fill_inverse then
+    ratio = 1 - ratio
+  end
+  _G.print('Update', 'dx', self.fill_x)
+  local x =
+      scale * ratio * directionCoord[self.fill_direction][1]
+  return min(x, scale) + self.fill_x
+end
+
+--- Determines the correct relative Y offset for a given progress ratio
+-- Factors padding and spacing parameters, including icon embedding dimensions.
+-- @param self frame on which the foreground/background textures and config data exist
+-- @param ratio progess from 0 to 1; 0 returns the starting corner coordinate, 1 returns end corner
+-- @param scale overrides the pixel fill length, with starting corner used as scaling origin
+local TranslateY    = function(self, ratio, scale)
+  if not scale then
+    scale = self.fill_height
+  end
+  if self.fill_inverse then
+    ratio = 1 - ratio
+  end
+
+  _G.print('Update', 'dy', self.fill_y)
+  -- (height of bar offset by the size of any overlapping element) * progress ratio, offset by the size of any overlapping embed + inset
+  local y =
+      scale * ratio * directionCoord[self.fill_direction][2] - self.foreground_inset
+  return min(y, scale) + self.fill_y
+end
+local SetProgress = function(self, progress, scaleX, scaleY)
+  _G.print('Update', cText(self:GetName()),
+    "\n     d:", progress, 'dX:', directionCoord[self.fill_direction][1], 'dY:', directionCoord[self.fill_direction][2], 'dI:', self.fill_inverse,
+    "\n  peak:", self.fill_anchor, self.fill_anchorTo, TranslateX(self, progress), TranslateY(self,progress))
+
+  self.foreground:SetPoint(self.fill_anchor, self.background, self.fill_anchorTo, TranslateX(self, progress, scaleX), TranslateY(self, progress, scaleY))
+end
+
+--- Assigns textures to the generic foreground background bar regions
+-- Called as F.SetStatusTextures(MyFrame, config pointer)
+-- foreground_inset - number of pixels by which the foreground edges occlude the background (basically always negative)
+-- padding - number of pixels between the background edge and frame edge (basically always positive)
+function F:SetStatusTextures(c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = self.db or lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+
+  self.fill_direction = c.fill_direction or 'RIGHT'
+  self.fill_inset = c.padding - c.foreground_inset  -- foreground edge / frame edge
+  self.fill_width = self.width - self.fill_inset*2       -- foreground left / right edges
+  self.fill_height = self.height - self.fill_inset*2     --           " top / bottom "
+  self.fill_inverse = c.fill_inverse
+  self.fill_x, self.fill_y = 0, 0
+  self.fill_insets = {
+    LEFT = self.fill_inset,
+    TOP = self.fill_inset,
+    BOTTOM = self.fill_inset,
+    RIGHT = self.fill_inset
+  }
+
+
+  -- create filling points
+  self.fill_base     = directionBase[self.fill_direction]
+  self.fill_anchor   =   directionPeak[self.fill_direction]
+  self.fill_anchorTo =   directionPeakTo[self.fill_direction]
+  print("   foreground:", self.fill_base, self.fill_base, TranslateX(self, 0), TranslateY(self, 0))
+  print("   foreground fill:", self.fill_anchor, self.fill_anchorTo)
+
+  -- calculate icon embed
+  if self.icon and self.icon.embedded then
+    print(cText('#### EMBED ####'))
+    -- change anchor point so the icon is inside
+    self.icon.anchor = self.icon.anchorTo
+    print('   * Icon anchor:' , cText(self.icon.anchor), 'to', cText(self.icon.anchorTo))
+    print('   * Fill start:', cText(self.fill_base))
+
+
+    local coordSet = {nil, nil}
+    local baseSet = {nil, nil }
+    local endSet = {nil, nil}
+    for dir, coords in pairs(embedScale) do
+      if self.icon.anchor:match(dir) then
+        print('   xtrans { matches', dir, 'include {', coords[1], coords[2], '}')
+        if not coordSet[1] then coordSet[1] = coords[1] end
+        if not coordSet[2] then coordSet[2] = coords[2] end
+
+        -- the embedding position can overlap either corner
+        if self.fill_base:match(dir) then
+          print('   base corner also matches')
+          if not baseSet[1] then baseSet[1] = coords[1] end
+          if not baseSet[2] then baseSet[2] = coords[2] end
+        else
+
+          if not endSet[1] then endSet[1] = coords[1] end
+          if not endSet[2] then endSet[2] = coords[2] end
+        end
+      end
+    end
+
+    -- make sure there are values if none of them matched at all for some reason
+    coordSet = {coordSet[1] or 0, coordSet[2] or 0}
+    baseSet = {baseSet[1] or 0, baseSet[2] or 0 }
+    -- needs to produce a negative number
+    endSet = {endSet[1] or 0, endSet[2] or 0 }
+
+
+    print('  == xtrans push =', unpack(coordSet))
+    print('  == fbase delta =', unpack(baseSet))
+    print('  == ftail delta =', unpack(endSet))
+
+    -- determine the foreground displacement
+
+    self.icon_dx = (min(self.fill_width, self.fill_height) + self.spacing)
+    self.icon_dy = (min(self.fill_width, self.fill_height) + self.spacing)
+    print('   * Foreground compression:', cNum(self.icon_dx))
+
+    self.icon_size = self.icon.size
+    self.icon_x = self.padding * baseSet[1] - (self.icon.size - self.icon_dx)
+    self.icon_y = self.padding * baseSet[2] - (self.icon.size - self.icon_dy)
+    print('   * Icon dims:' , cNum(self.icon_size), ' offset:', cNum(self.icon_dx))
+
+
+    local ofi = self.fill_insets[self.fill_direction]
+    print('   * Fill inset('..cWord(self.fill_direction)..') from', cNum(ofi), 'to', cNum(self.fill_insets[self.fill_direction]))
+
+    -- used to place the starting corner
+    self.fill_x = (self.icon_dx) * baseSet[1]
+    self.fill_y = (self.icon_dy) * baseSet[2]
+    print('   * fill offset dX:', self.fill_x)
+    print('   * fill offset dY:', self.fill_y)
+
+    -- amount taken off of fill scale, subtract spacing
+    self.icon_dx_cut = abs(self.icon_dx * -baseSet[1])
+    self.icon_dy_cut = abs(self.icon_dy * -baseSet[2])
+
+    local ofw, ofh = self.fill_width, self.fill_height
+    self.fill_width = self.fill_width - self.icon_dx_cut
+    self.fill_height = self.fill_height - self.icon_dy_cut
+    print('   * Scale dX:', self.icon_dx_cut, cNum(ofw), 'to', cNum(self.fill_width))
+    print('   * Scale dY:', self.icon_dy_cut, cNum(ofh), 'to', cNum(self.fill_height))
+
+    self.icon:ClearAllPoints()
+    self.icon:SetPoint(self.icon.anchor, self, self.icon.anchorTo, self.icon_x, self.padding * coordSet[2])
+    self.icon:SetSize(self.icon_size, self.icon_size)
+  end
+
+  --@debug@
+  print(namet('SetStatusTextures'),'(', valuef(self:GetName()), ')')
+  print('   form:', self.padding,'-', self.foreground_inset, '+', self.fill_width, self.padding,'-', self.foreground_inset, '+','x', self.padding,'-', self.foreground_inset, '+', self.fill_height, self.padding,'-', self.foreground_inset, '+')
+  print('  ', valuen(concat(c.background_color,', ')),valuet(c.background_texture), valuen(concat(c.foreground_color,', ')), valuet(c.foreground_texture))
+  print('   background:', self.padding, self.padding, 'BOTTOMLEFT', 'BOTTOMLEFT' , '::', -self.padding, -self.padding, 'TOPRIGHT', 'TOPRIGHT')
+  --@end-debug@
+
+  self.background:ClearAllPoints()
+  self.background:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', self.padding, self.padding)
+  self.background:SetPoint('TOPRIGHT', self, 'TOPRIGHT', -self.padding, -self.padding)
+  if c.background_texture ~= '' and c.background_texture ~= nil then
+    self.background:SetTexture(c.background_texture)
+    self.background:SetVertexColor(unpack(c.background_color))
+  else
+    self.background:SetTexture(unpack(c.background_color))
+  end
+  self.background:SetBlendMode(c.background_blend)
+
+  if c.foreground_texture ~= '' and c.foreground_texture ~= nil then
+    self.foreground:SetTexture(c.foreground_texture)
+    self.foreground:SetVertexColor(unpack(c.foreground_color))
+  else
+    self.foreground:SetTexture(unpack(c.foreground_color))
+  end
+  self.foreground:SetBlendMode(c.foreground_blend)
+
+  self.foreground:ClearAllPoints()
+  local dx = (directionCoord[self.fill_direction][3] * -self.foreground_inset) + self.fill_x
+  local dy = (directionCoord[self.fill_direction][4] * -self.foreground_inset) + self.fill_y
+  print('   foreground base:', cNum(dx), cNum(dy))
+  self.foreground:SetPoint(self.fill_base, self.background, self.fill_base, dx, dy)
+
+  SetProgress(self, 1)
+
+  self.TranslateX = TranslateX
+  self.TranslateY = TranslateY
+  self.SetFillPoints = SetFillPoints
+  self.SetProgress = SetProgress
+end
+--- Sets properties for a generated FontString
+-- Takes the FontString object as a self, and applies config values.
+-- In particalur, the anchor parent is derived from either a fixed number of GetParent() or global name
+-- @usage Region:SetFontLayout([table])
+function F:SetFontLayout (c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+  --@debug@
+  print(namet('FontLayout'),'(', valuet(self:GetName()), ')', valuet(c.font), valuet(c.size), valuet(c.outline))
+  print('  ', valuen(c.anchor or 'CENTER'), relativeTo:GetName(), valuen(c.anchorTo or 'CENTER'), valued(c.x or 0),
+    valued(c.y or 0))
+  --@end-debug@
+  self:SetPoint(
+    c.anchor or 'CENTER',
+    relativeTo,
+    c.anchorTo or 'CENTER',
+    c.x or 0,
+    c.y or 0)
+  self:SetSize(
+    c.width or relativeTo:GetWidth(),
+    c.height or relativeTo:GetHeight())
+  self:SetFont(
+    c.font,
+    c.size,
+    c.outline)
+  self:SetJustifyH(c.justifyH or 'CENTER')
+  self:SetJustifyV(c.justifyV or 'MIDDLE')
+  self:SetTextColor(unpack(c.text_color or {1,1,1,1}))
+end
+
+--- For setting up widget pieces
+function F:SetTextureLayout(c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+  self.size = c.size
+  self.width = c.width
+  self.height = c.height
+  self.x = c.x
+  self.y = c.y
+  self.embedded = c.embedded
+  self.anchor = c.anchor
+  self.anchorTo = c.anchorTo
+
+  self:SetPoint(c.anchor, relativeTo, c.anchorTo, c.x, c.y)
+  self:SetSize(c.size or c.width, c.size or c.height)
+  self:SetAlpha(c.alpha)
+  self:SetDesaturated(c.desaturated or false)
+  print('|cFF00FFFFSetTextureLayout(|r', self:GetName(), '|cFF00FFFF)|r')
+  print('  ', c.anchor, relativeTo, c.anchorT, c.x, c.v)
+  if c.combatFade and not (relativeTo.faderID and F.animate_regions[relativeTo.faderID]) then
+    tinsert(F.animate_regions, self)
+    self.faderID = #F.animate_regions
+    --@debug@
+    print('register fadeable texture #'..self.faderID)
+    --@end-debug@
+  end
+end
+
+local alphaSubType = {
+  [1] = '_passive',
+  [2] = '_active'
+}
+local alphaFillType = {
+  [1] = '_empty',
+  [2] = '_half',
+  [3] = '_full',
+}
+function F:UpdateAlpha(inCombat, displayState, fillState)
+  local print = function() end
+  print(cWord('UpdateAlpha(')..self:GetName()..cWord(')'))
+  local alphaType = inCombat and 'alpha' or 'alpha_ooc'
+  local alphaDuration = inCombat and 'alpha_fade_in' or 'alpha_fade_out'
+  local displayState = displayState or self.displayState or nil
+  local fillState = fillState or self.fillState or nil
+
+  local alphaSubType = ''
+  if displayState then
+    alphaSubType = (displayState == 1) and '_passive' or ((displayState == 2) and '_active' or '' )
+  end
+
+  local alphaFillType = ''
+  if fillState then
+    alphaFillType = (fillState == 1) and '_half' or ((fillState == 2) and '_full' or '_empty')
+  end
+
+  local fadeTo = self[alphaType..alphaSubType..alphaFillType]
+  local fadeDuration = self[alphaDuration] or 0
+  print(' alphaKey:', cWord(alphaType..alphaSubType..alphaFillType))
+  print(' alphaTo:', cNum(fadeTo), 'duration:', cNum(fadeDuration))
+  if self:IsVisible() and fadeDuration ~= 0 then
+    self:Fade(fadeDuration, fadeTo or (inCombat and 1 or 0.5), true)
+    print('   |cFFFFFF00  :Fade()|r', 'dur='..cNum(fadeDuration), cNum(fadeTo), cWord(self:GetName()))
+  else
+    self:SetAlpha(fadeTo)
+    print('   |cFF00FF00  :SetAlpha('..fadeTo..')|r', cText(self:GetName()))
+  end
+end
+
+--- Sets an OnUpdate within a time-throttled wrapper
+-- throttle rate should be slightly smaller than 1/average frame rate
+function F:SetFrameScript(updateFunc, showFunc, hideFunc, ...)
+  local print = GetPrint(self.trace)
+
+  if not self.__oldscripts then
+    self.__oldscripts = {}
+  end
+
+  self:SetScript('OnUpdate', nil) -- clear any scripts
+  self:SetSCript('OnUpdate', function(self)
+    if GetTime() < self.throttle_time then
+      return
+    end
+    self.throttle_time = self.throttle_time + self.throttle_rate
+    updateFunc(self)
+  end) -- put the new function in its place
+  local changeSet = {
+    { self.__oldscripts,onUpdate = self:GetScript('OnUpdate') },
+  }
+
+  if showFunc then
+    tinsert(changeSet,
+      {onShow = self:GetScript('OnShow')})
+    self:SetScript('OnShow', showFunc)
+  end
+
+  if hideFunc then
+    tinsert(changeSet,
+      {onHide = self:GetScript('OnShow')})
+    self:SetScript('OnHide', hideFunc)
+  end
+end
+
+--- Embed
+function F:Embed(object)
+  print('Doing embed')
+  for k, v in pairs(ui_embeds) do
+    print('embedding Set'..k..'Layout')
+    object['Set'..k..'Layout'] = self[v]
+  end
+  object.SetStatusTextures = self.SetStatusTextures
+
+  --- map a generic layout method if embedded into a frame object
+  if object.GetObjectType and ui_embeds[object.GetObjectType()] then
+    if ui_embeds[object.GetObjectType()] then
+      object.SetLayout = ui_embeds[object.GetObjectType()]
+    end
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Layout/Layout.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,130 @@
+<Ui>
+  <Script file="Layout.lua" />
+
+  <Frame name="TurokStatusFrame" parent="UIParent" virtual="true" strata="MEDIUM" hidden="true">
+    <Scripts>
+      <OnShow>
+        self.throttle_point = 0
+        if self.labels then
+          for _,label in pairs(self.labels) do
+            label:Show()
+          end
+        end
+      </OnShow>
+      <OnHide>
+        if self.labels then
+          for _,label in pairs(self.labels) do
+            label:Hide()
+          end
+        end
+      </OnHide>
+      <OnUpdate>
+        if GetTime() &lt; self.throttle_point then
+          return
+        end
+        self.throttle_point = self.throttle_point + 0.010
+        if self.db and self.db.preUpdate then
+          self.db.preUpdate(self)
+        end
+        self:Update()
+        if self.db and self.db.postUpdate then
+          self.db.postUpdate(self)
+        end
+      </OnUpdate>
+    </Scripts>
+
+    <Backdrop bgFile="Interface\Addons\Turok\Media\border\BG-Solid.blp" edgeFile="Interface\Addons\Turok\Media\border\BigBorder-Solid.blp" tile="true">
+      <EdgeSize val="12"/>
+      <TileSize val="12"/>
+      <BorderColor r="1" g="1" b="1" a="1" />
+      <Color r="1" g="1" b="1" a="0" />
+      <BackgroundInsets>
+        <AbsInset left="2" right="2" top="2" bottom="2"/>
+      </BackgroundInsets>
+    </Backdrop>
+  </Frame>
+
+  <Frame name="TurokStatusBar" inherits="TurokStatusFrame" parent="UIParent" virtual="true" strata="HIGH" hidden="true">
+    <Scripts>
+      <OnLoad>
+        self.lefttext = self.overlay.lefttext
+        self.righttext = self.overlay.righttext
+      </OnLoad>
+    </Scripts>
+    <Layers>
+      <Layer level="BORDER">
+        <Texture parentKey="background" name="$parent.background" alphaMode="BLEND" />
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture parentKey="foreground" name="$parent.foreground" alphaMode="BLEND" textureSubLevel="0" />
+        <Texture name="$parentIcon" parentKey="icon" blendMode="BLEND" textureSubLevel="1">
+          <Anchors>
+            <Anchor point="RIGHT" relativePoint="LEFT" relativeTo="$parent" x="-2" y="0" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+    <Frames>
+      <Frame name="$parent_OverLay" parentKey="overlay">
+        <Layers>
+          <Layer point="OVERLAY">
+            <FontString name="$parent_LeftText" inherits="TurokFont" parentKey="lefttext">
+              <Anchors>
+                <Anchor point="LEFT" relativePoint="LEFT" x="6" y="0" />
+              </Anchors>
+            </FontString>
+
+            <FontString name="$parent_RightText" inherits="TurokFontDetail" parentKey="righttext">
+              <Anchors>
+                <Anchor point="RIGHT" relativePoint="RIGHT" x="-6" y="0" />
+              </Anchors>
+            </FontString>
+          </Layer>
+        </Layers>
+      </Frame>
+    </Frames>
+  </Frame>
+
+  <Font name="TurokFontBig" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf" outline="NORMAL">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="32"/>
+    </FontHeight>
+  </Font>
+  <Font name="TurokFontMed" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf" outline="NORMAL">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="24"/>
+    </FontHeight>
+  </Font>
+  <Font name="TurokFont" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf" outline="NORMAL">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="16"/>
+    </FontHeight>
+  </Font>
+  <Font name="TurokFontNormal" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf" outline="NONE">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="16"/>
+    </FontHeight>
+  </Font>
+  <Font name="TurokFontSmall" inherits="TurokFont">
+    <FontHeight>
+      <AbsValue val="14" />
+    </FontHeight>
+  </Font>
+
+  <Font name="TurokFontDetail" font="Interface\Addons\Turok\Media\font\ArchivoNarrow-Regular.ttf" outline="NORMAL">
+    <Color r="1" g="1" b="1" a="1" />
+    <FontHeight>
+      <AbsValue val="16"/>
+    </FontHeight>
+  </Font>
+
+  <Font name="TurokFontDetailSmall" inherits="TurokFontDetail">
+    <FontHeight>
+      <AbsValue val="14"/>
+    </FontHeight>
+  </Font>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Media.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,96 @@
+local LSM = LibStub("LibSharedMedia-3.0") 
+
+-- ----- 
+-- BACKGROUND 
+-- ----- 
+
+-- ----- 
+--  BORDER 
+-- ---- 
+LSM:Register("border", "BG-Solid", [[Interface\Addons\Turok\Media\border\BG-Solid.blp]]) 
+LSM:Register("border", "BG-Solid", [[Interface\Addons\Turok\Media\border\BG-Solid.png]]) 
+LSM:Register("border", "BigBorder-Solid", [[Interface\Addons\Turok\Media\border\BigBorder-Solid.blp]]) 
+LSM:Register("border", "BigBorder-Solid", [[Interface\Addons\Turok\Media\border\BigBorder-Solid.png]]) 
+
+-- -----
+--   FONT
+-- -----
+LSM:Register("font", "ArchivoNarrow-Bold", [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf]]) 
+LSM:Register("font", "ArchivoNarrow-BoldItalic", [[Interface\Addons\Turok\Media\font\ArchivoNarrow-BoldItalic.ttf]]) 
+LSM:Register("font", "ArchivoNarrow-Italic", [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Italic.ttf]]) 
+LSM:Register("font", "ArchivoNarrow-Regular", [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Regular.ttf]]) 
+LSM:Register("font", "Arvo-Bold", [[Interface\Addons\Turok\Media\font\Arvo-Bold.ttf]]) 
+LSM:Register("font", "Arvo-BoldItalic", [[Interface\Addons\Turok\Media\font\Arvo-BoldItalic.ttf]]) 
+LSM:Register("font", "Arvo-Italic", [[Interface\Addons\Turok\Media\font\Arvo-Italic.ttf]]) 
+LSM:Register("font", "Arvo-Regular", [[Interface\Addons\Turok\Media\font\Arvo-Regular.ttf]]) 
+LSM:Register("font", "constan", [[Interface\Addons\Turok\Media\font\constan.ttf]]) 
+LSM:Register("font", "DroidSerif-Bold", [[Interface\Addons\Turok\Media\font\DroidSerif-Bold.ttf]]) 
+LSM:Register("font", "DroidSerif-BoldItalic", [[Interface\Addons\Turok\Media\font\DroidSerif-BoldItalic.ttf]]) 
+LSM:Register("font", "DroidSerif-Italic", [[Interface\Addons\Turok\Media\font\DroidSerif-Italic.ttf]]) 
+LSM:Register("font", "DroidSerif", [[Interface\Addons\Turok\Media\font\DroidSerif.ttf]]) 
+LSM:Register("font", "OpenSans-Bold", [[Interface\Addons\Turok\Media\font\OpenSans-Bold.ttf]]) 
+LSM:Register("font", "OpenSans-BoldItalic", [[Interface\Addons\Turok\Media\font\OpenSans-BoldItalic.ttf]]) 
+LSM:Register("font", "OpenSans-ExtraBold", [[Interface\Addons\Turok\Media\font\OpenSans-ExtraBold.ttf]]) 
+LSM:Register("font", "OpenSans-ExtraBoldItalic", [[Interface\Addons\Turok\Media\font\OpenSans-ExtraBoldItalic.ttf]]) 
+LSM:Register("font", "OpenSans-Italic", [[Interface\Addons\Turok\Media\font\OpenSans-Italic.ttf]]) 
+LSM:Register("font", "OpenSans-Light", [[Interface\Addons\Turok\Media\font\OpenSans-Light.ttf]]) 
+LSM:Register("font", "OpenSans-LightItalic", [[Interface\Addons\Turok\Media\font\OpenSans-LightItalic.ttf]]) 
+LSM:Register("font", "OpenSans-Regular", [[Interface\Addons\Turok\Media\font\OpenSans-Regular.ttf]]) 
+LSM:Register("font", "OpenSans-SemiboldItalic", [[Interface\Addons\Turok\Media\font\OpenSans-SemiboldItalic.ttf]]) 
+LSM:Register("font", "PT_Sans-Narrow-Web-Bold", [[Interface\Addons\Turok\Media\font\PT_Sans-Narrow-Web-Bold.ttf]]) 
+LSM:Register("font", "PT_Sans-Narrow-Web-Regular", [[Interface\Addons\Turok\Media\font\PT_Sans-Narrow-Web-Regular.ttf]]) 
+LSM:Register("font", "Roboto-Black", [[Interface\Addons\Turok\Media\font\Roboto-Black.ttf]]) 
+LSM:Register("font", "Roboto-BlackItalic", [[Interface\Addons\Turok\Media\font\Roboto-BlackItalic.ttf]]) 
+LSM:Register("font", "Roboto-Bold", [[Interface\Addons\Turok\Media\font\Roboto-Bold.ttf]]) 
+LSM:Register("font", "Roboto-BoldItalic", [[Interface\Addons\Turok\Media\font\Roboto-BoldItalic.ttf]]) 
+LSM:Register("font", "Roboto-Italic", [[Interface\Addons\Turok\Media\font\Roboto-Italic.ttf]]) 
+LSM:Register("font", "Roboto-Light", [[Interface\Addons\Turok\Media\font\Roboto-Light.ttf]]) 
+LSM:Register("font", "Roboto-LightItalic", [[Interface\Addons\Turok\Media\font\Roboto-LightItalic.ttf]]) 
+LSM:Register("font", "Roboto-Medium", [[Interface\Addons\Turok\Media\font\Roboto-Medium.ttf]]) 
+LSM:Register("font", "Roboto-MediumItalic", [[Interface\Addons\Turok\Media\font\Roboto-MediumItalic.ttf]]) 
+LSM:Register("font", "Roboto-Regular", [[Interface\Addons\Turok\Media\font\Roboto-Regular.ttf]]) 
+LSM:Register("font", "Roboto-Thin", [[Interface\Addons\Turok\Media\font\Roboto-Thin.ttf]]) 
+LSM:Register("font", "Roboto-ThinItalic", [[Interface\Addons\Turok\Media\font\Roboto-ThinItalic.ttf]]) 
+LSM:Register("font", "RobotoSlab-Bold", [[Interface\Addons\Turok\Media\font\RobotoSlab-Bold.ttf]]) 
+LSM:Register("font", "RobotoSlab-Light", [[Interface\Addons\Turok\Media\font\RobotoSlab-Light.ttf]]) 
+LSM:Register("font", "RobotoSlab-Regular", [[Interface\Addons\Turok\Media\font\RobotoSlab-Regular.ttf]]) 
+LSM:Register("font", "RobotoSlab-Thin", [[Interface\Addons\Turok\Media\font\RobotoSlab-Thin.ttf]]) 
+LSM:Register("font", "SourceCodePro-Black", [[Interface\Addons\Turok\Media\font\SourceCodePro-Black.ttf]]) 
+LSM:Register("font", "SourceCodePro-Bold", [[Interface\Addons\Turok\Media\font\SourceCodePro-Bold.ttf]]) 
+LSM:Register("font", "SourceCodePro-ExtraLight", [[Interface\Addons\Turok\Media\font\SourceCodePro-ExtraLight.ttf]]) 
+LSM:Register("font", "SourceCodePro-Light", [[Interface\Addons\Turok\Media\font\SourceCodePro-Light.ttf]]) 
+LSM:Register("font", "SourceCodePro-Medium", [[Interface\Addons\Turok\Media\font\SourceCodePro-Medium.ttf]]) 
+LSM:Register("font", "SourceCodePro-Regular", [[Interface\Addons\Turok\Media\font\SourceCodePro-Regular.ttf]]) 
+LSM:Register("font", "SourceCodePro-Semibold", [[Interface\Addons\Turok\Media\font\SourceCodePro-Semibold.ttf]]) 
+LSM:Register("font", "turok", [[Interface\Addons\Turok\Media\font\turok.ttf]]) 
+
+-- -----
+--   SOUND
+-- -----
+LSM:Register("sound", "Bell", [[Interface\Addons\Turok\Media\sound\Bell.ogg]]) 
+LSM:Register("sound", "Chime", [[Interface\Addons\Turok\Media\sound\Chime.ogg]]) 
+LSM:Register("sound", "Electro_-S_Bainbr-7953_hifi", [[Interface\Addons\Turok\Media\sound\Electro_-S_Bainbr-7953_hifi.mp3]]) 
+LSM:Register("sound", "Electro_-S_Bainbr-7955_hifi", [[Interface\Addons\Turok\Media\sound\Electro_-S_Bainbr-7955_hifi.mp3]]) 
+LSM:Register("sound", "FLASH", [[Interface\Addons\Turok\Media\sound\FLASH.mp3]]) 
+LSM:Register("sound", "Heart", [[Interface\Addons\Turok\Media\sound\Heart.ogg]]) 
+LSM:Register("sound", "High_Bee-Public_D-135_hifi", [[Interface\Addons\Turok\Media\sound\High_Bee-Public_D-135_hifi.mp3]]) 
+LSM:Register("sound", "IM", [[Interface\Addons\Turok\Media\sound\IM.ogg]]) 
+LSM:Register("sound", "Info", [[Interface\Addons\Turok\Media\sound\Info.ogg]]) 
+LSM:Register("sound", "Kachink", [[Interface\Addons\Turok\Media\sound\Kachink.ogg]]) 
+LSM:Register("sound", "Link", [[Interface\Addons\Turok\Media\sound\Link.ogg]]) 
+LSM:Register("sound", "Low_Beep-Public_D-136_hifi", [[Interface\Addons\Turok\Media\sound\Low_Beep-Public_D-136_hifi.mp3]]) 
+LSM:Register("sound", "Oringz-881", [[Interface\Addons\Turok\Media\sound\Oringz-881.mp3]]) 
+LSM:Register("sound", "Oringz-940", [[Interface\Addons\Turok\Media\sound\Oringz-940.mp3]]) 
+LSM:Register("sound", "polish-xrikazen-7425_hifi", [[Interface\Addons\Turok\Media\sound\polish-xrikazen-7425_hifi.mp3]]) 
+LSM:Register("sound", "Quack", [[Interface\Addons\Turok\Media\sound\Quack.ogg]]) 
+LSM:Register("sound", "SquishFart", [[Interface\Addons\Turok\Media\sound\SquishFart.ogg]]) 
+LSM:Register("sound", "Text1", [[Interface\Addons\Turok\Media\sound\Text1.ogg]]) 
+LSM:Register("sound", "Text2", [[Interface\Addons\Turok\Media\sound\Text2.ogg]]) 
+LSM:Register("sound", "wilhelm", [[Interface\Addons\Turok\Media\sound\wilhelm.ogg]]) 
+LSM:Register("sound", "Xylo", [[Interface\Addons\Turok\Media\sound\Xylo.ogg]]) 
+
+-- -----
+--   STATUSBAR
+-- -----
+LSM:Register("statusbar", "Minimalist", [[Interface\Addons\Turok\Media\statusbar\Minimalist.tga]]) 
+LSM:Register("statusbar", "Otravi", [[Interface\Addons\Turok\Media\statusbar\Otravi.tga]]) 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Media/Manifest.bat.txt	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,59 @@
+@echo off
+echo This script will create our assets manifest for LibSharedMedia
+
+
+echo Creating the file...
+echo local LSM = LibStub("LibSharedMedia-3.0") > ..\Media.lua
+
+echo    BACKGROUND
+echo.>> ..\Media.lua
+echo -- ----- >> ..\Media.lua
+echo -- BACKGROUND >> ..\Media.lua
+echo -- ----- >> ..\Media.lua
+for %%F in (..\Media\background\*.*) do (
+echo       %%~nF
+echo LSM:Register("background", "%%~nF", [[Interface\Addons\Turok\Media\background\%%~nxF]]^) >> ..\Media.lua
+)
+
+echo    BORDER
+echo.>> ..\Media.lua
+echo -- ----- >> ..\Media.lua
+echo --  BORDER >> ..\Media.lua
+echo -- ---- >> ..\Media.lua
+for %%F in (..\Media\border\*.*) do (
+echo       %%~nF
+echo LSM:Register("border", "%%~nF", [[Interface\Addons\Turok\Media\border\%%~nxF]]^) >> ..\Media.lua
+)
+
+echo    FONT
+echo.>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+echo --   FONT>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+for %%F in (..\Media\font\*.ttf) do (
+echo       %%~nF
+echo LSM:Register("font", "%%~nF", [[Interface\Addons\Turok\Media\font\%%~nxF]]^) >> ..\Media.lua
+)
+
+echo    SOUND
+echo.>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+echo --   SOUND>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+for %%F in (..\Media\sound\*.*) do (
+echo       %%~nF
+echo LSM:Register("sound", "%%~nF", [[Interface\Addons\Turok\Media\sound\%%~nxF]]^) >> ..\Media.lua
+)
+
+echo    STATUSBAR
+echo.>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+echo --   STATUSBAR>> ..\Media.lua
+echo -- ----->> ..\Media.lua
+for %%F in (..\Media\statusbar\*.*) do (
+echo       %%~nF
+echo LSM:Register("statusbar", "%%~nF", [[Interface\Addons\Turok\Media\statusbar\%%~nxF]]^) >> ..\Media.lua
+)
+
+:end_of_file
+pause
\ No newline at end of file
Binary file Turok/Media/border/BG-Solid.blp has changed
Binary file Turok/Media/border/BG-Solid.png has changed
Binary file Turok/Media/border/BigBorder-Solid.blp has changed
Binary file Turok/Media/border/BigBorder-Solid.png has changed
Binary file Turok/Media/sound/Bell.ogg has changed
Binary file Turok/Media/sound/Chime.ogg has changed
Binary file Turok/Media/sound/Electro_-S_Bainbr-7953_hifi.mp3 has changed
Binary file Turok/Media/sound/Electro_-S_Bainbr-7955_hifi.mp3 has changed
Binary file Turok/Media/sound/FLASH.mp3 has changed
Binary file Turok/Media/sound/Heart.ogg has changed
Binary file Turok/Media/sound/High_Bee-Public_D-135_hifi.mp3 has changed
Binary file Turok/Media/sound/IM.ogg has changed
Binary file Turok/Media/sound/Info.ogg has changed
Binary file Turok/Media/sound/Kachink.ogg has changed
Binary file Turok/Media/sound/Link.ogg has changed
Binary file Turok/Media/sound/Low_Beep-Public_D-136_hifi.mp3 has changed
Binary file Turok/Media/sound/Oringz-881.mp3 has changed
Binary file Turok/Media/sound/Oringz-940.mp3 has changed
Binary file Turok/Media/sound/Quack.ogg has changed
Binary file Turok/Media/sound/SquishFart.ogg has changed
Binary file Turok/Media/sound/Text1.ogg has changed
Binary file Turok/Media/sound/Text2.ogg has changed
Binary file Turok/Media/sound/Xylo.ogg has changed
Binary file Turok/Media/sound/polish-xrikazen-7425_hifi.mp3 has changed
Binary file Turok/Media/statusbar/Aluminium.tga has changed
Binary file Turok/Media/statusbar/Armory.tga has changed
Binary file Turok/Media/statusbar/Minimalist.tga has changed
Binary file Turok/Media/statusbar/Otravi.tga has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Castbar.Init.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,124 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/28/2015 8:00 AM
+
+Turok.defaults.tek = {
+  width = 300, height = 30,
+  anchor = 'CENTER', parent = 'UIParent', anchorTo = 'CENTER',
+  x = 0, y = 0,
+  foreground_blend = 'BLEND',
+  background_blend = 'BLEND',
+  background_texture = '',
+  background_color = {1,1,1,0.2},
+  combatFade = false,
+
+  foreground_channeling = {0.4,0.2,0.9,1},
+  background_channeling = {0.25,0.25,0.25,1},
+  foreground_casting = {0.01, 0.46, 0.93, 1},
+  background_casting = {0,0,0,0.0},
+  background_interrupted = {1,0,0,1},
+  foreground_interrupted = {1, 0.5, 0, 1},
+  foreground_uninterruptible = {0.8, 0.8, .8, 1},
+  background_uninterruptible = {0,0,0,0.25},
+  background_failed = {0.25,0.25,0.25, 1},
+  foreground_failed = {1,0.5,0.5,0.25},
+  foreground_finished = {0.4,.7,.1, 1},
+  background_finished = {0,0,0,0.25},
+  foreground_inset = 0,
+  padding = 2,
+  spacing = 1,
+
+  icon = {
+    size = 56,
+    anchor = 'RIGHT', anchorTo = 'LEFT',
+    parent = 1,
+    x = 0, y = 0,
+  },
+
+  fill_direction = 'RIGHT',
+
+  glow_texture = "Tooltip-BigBorder",
+  glow_size = 2,
+  spark_texture = "",
+  spark_size = 2,
+  spellname = {},
+  casttime = {},
+  ping = {
+    anchor='TOPRIGHT',
+    anchorTo='BOTTOMRIGHT',
+    x = -6, y= 0,
+    parent = 1,
+    parentKey = 'background',
+    color = {1,1,0,0.7},
+    size = 12,
+    font = "Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Bold.ttf",
+    justifyH = 'RIGHT',
+    justifyV = 'BOTTOM'
+  },
+  casttime = {
+    parent = 1,
+    parentKey = 'background',
+    x = -6, y= 0,
+    anchor = 'RIGHT',
+    anchorTo = 'RIGHT',
+    justifyH = 'RIGHT',
+    justifyV = 'MIDDLE'
+  },
+  spelltext = {
+    parent = 1,
+    parentKey = 'foreground',
+    x = 6, y = 0,
+    anchor = 'LEFT',
+    anchorTo = 'LEFT',
+    justifyH = 'LEFT',
+    justifyV = 'MIDDLE',
+  },
+  downtime = {
+    parent = 1,
+    parentKey = 'foreground',
+    anchor='TOPLEFT',
+    anchorTo='BOTTOMLEFT',
+    x = 0, y = 0,
+    text_color = {.75,.75,.75,1},
+    justifyH = 'LEFT',
+  },
+  ['player'] = {
+    icon = {
+      embedded = true,
+      size = 36,
+      anchor = 'RIGHT', anchorTo = 'LEFT',
+      parent = 1,
+      x = -2, y = 0,
+    },
+    width = 330, height = 32,
+    anchor = 'TOP', parent = 'UIParent', anchorTo = 'CENTER',
+    x = 0, y = -254,
+  },
+  ['target'] = {
+    icon = {
+      embedded = true,
+      size = 36,
+      width = 300,
+      anchor = 'LEFT', anchorTo = 'LEFT',
+      parent = 'TekplayerCastBar',
+      x = -2, y = 0,
+    },
+
+    width = 300, height = 24,
+    height = 36,
+    anchor = 'BOTTOMLEFT', parent = 'TekplayerCastBar', anchorTo = 'TOPRIGHT',
+    x = 1, y =1,
+  },
+  ['focus'] = {
+    width = 200, height = 36,
+    anchor = 'TOPLEFT', parent = 'TekplayerCastBar', anchorTo='BOTTOMRIGHT',
+    x = 2, y = -2
+  },
+  ['pet'] = {
+    width = 300, height = 24,
+    anchor = 'TOPRIGHT', parent = 'TekplayerCastBar', anchorTo='TOPLEFT',
+    x = -2, y = 0
+  },
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Castbar.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,510 @@
+--- Turok
+-- Castbar.lua
+-- Created: 12/4/2015 11:17 PM
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Deals with casting events and works the castingbar interface
+local print, tostring, tonumber, rep, pairs, min, format, unpack, select, strpad = print, tostring, tonumber, string.rep, pairs, math.min, string.format, unpack, select, string.rep
+local GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo = GetTime, UnitCastingInfo, UnitChannelInfo, GetSpellInfo, GetSpellTabInfo
+local floor = math.floor
+local T = Turok
+local db
+local PING_CEILING = 190 -- transpacific
+local PING_MIDLINE = 100
+local PING_FLOOR = 10 -- transcontinental
+--@debug
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Tek', ...)
+  end
+end
+
+local EVENT_COLOR = '|cFF44FF44'
+local EVENT_COLOR2 = '|cFF88FF88'
+local DATA_COLOR = '|cFFDD77DD'
+--@end-debug@
+
+local mod = T:NewModule("Tek")
+function mod:OnInitialize()
+  self.db = T.db.tek
+  self.castbar = {}
+  self.UNIT_SPELLCAST_SENT          = self.SpellCastRequest
+  self.UNIT_SPELLCAST_START           = self.SpellCastEvent
+  self.UNIT_SPELLCAST_DELAYED         = self.SpellCastEvent
+  self.UNIT_SPELLCAST_SUCCEEDED       = self.SpellCastEvent
+  self.UNIT_SPELLCAST_STOP            = self.SpellCastEvent
+  self.UNIT_SPELLCAST_FAILED          = self.SpellCastEvent
+  self.UNIT_SPELLCAST_INTERRUPTED     = self.SpellCastEvent
+  self.UNIT_SPELLCAST_CHANNEL_START   = self.SpellCastEvent
+  self.UNIT_SPELLCAST_CHANNEL_STOP    = self.SpellCastEvent
+  self.UNIT_SPELLCAST_CHANNEL_UPDATE  = self.SpellCastEvent
+  self.UNIT_SPELLCAST_INTERRUPTIBLE   = self.SpellCastEvent
+  self.UNIT_SPELLCAST_UNINTERRUPTIBLE = self.SpellCastEvent
+end
+
+--- events & units
+local TRACKED_UNITS = {'player', 'target', 'focus', 'pet'}
+local FADE_OUT_TIME, FADE_IN_TIME = 1, .2
+
+local TEXTURE_SUFFIX = {
+  ['CHANNEL_START'] = '_channeling',
+  ['CHANNEL_UPDATE'] = '_channeling',
+  ['CHANNEL_STOPPED'] = '_channeling',
+  ['START'] = '_casting',
+  ['INTERRUPTED'] = '_interrupted',
+  ['SUCCEEDED'] = '_finished',
+}
+
+function mod:OnEnable()
+  db = self.db
+  -- setup castingbar frames
+  local c = self.castbar
+  for _, unit in pairs(TRACKED_UNITS) do
+    local cdb = db[unit]
+    --@debug@
+    print(DATA_COLOR .. unit .. '|r castbar creation')--@end-debug@
+
+    -- Set frames
+    local fn = 'Tek' .. unit .. 'CastBar'
+    c[unit] = CreateFrame('Frame', fn, _G[cdb.parent], 'TurokCastingBar')
+
+    local pc = c[unit]
+    T.SetFrameLayout(pc, cdb)
+    T.SetTextureLayout(pc.icon, cdb.icon or db.icon)
+    T.SetStatusTextures(pc, cdb.statusbar or db.statusbar)
+
+    if unit == 'player' then
+      T.SetFontLayout(pc.ping, db.ping)
+      T.SetFontLayout(pc.downtime, db.downtime)
+      pc.pingbar:ClearAllPoints()
+      pc.pingbar:SetPoint('TOPRIGHT', pc.background, 'TOPRIGHT', 0, 0)
+      pc.pingbar:SetHeight(pc.background:GetHeight())
+    else
+      if pc.interrupt then
+        pc.interrupt:SetSize(pc.icon.size*3, pc.icon.size* 3)
+      end
+
+    end
+
+    T.SetFontLayout(pc.casttime, cdb.casttime or db.casttime)
+    T.SetFontLayout(pc.spelltext, cdb.spelltext or db.spelltext)
+
+    pc:SetAlpha(0)
+    pc.last = nil
+    pc.sent = {}
+    pc.unit = unit
+    pc.SetSpell = self.SetSpell
+    pc.SetState = self.SetState
+    pc.Update = self.Update
+  end
+
+
+  -- kill default casting bar
+  -- T.cbscripts = {CastingBarFrame:GetScript('OnUpdate'), CastingBarFrame:GetScript('OnEvent')}
+  CastingBarFrame:SetScript('OnUpdate', nil)
+  CastingBarFrame:SetScript('OnEvent', nil)
+  CastingBarFrame:Hide()
+  PetCastingBarFrame:SetScript('OnUpdate', nil)
+  PetCastingBarFrame:SetScript('OnEvent', nil)
+  PetCastingBarFrame:Hide()
+end
+
+function mod:UpdateLocked()
+    for k, v in pairs(self.castbar) do
+      if T.unlocked then
+        local name, texture, offset, numSpells = GetSpellTabInfo(T.specPage)
+        v.value = offset
+        v.duration = numSpells
+        v.spelltext:SetText(k)
+        v.icon:SetTexture(texture)
+        v:SetAlpha(1)
+        v:Show()
+        v:EnableMouse(true)
+        v:SetMovable(true)
+        v:RegisterForDrag("LeftButton")
+        v:SetScript('OnDragStart', function(self)  self:StartMoving() end)
+        v:SetScript('OnDragStop', function(self) self:StopMovingOrSizing() end)
+      else
+
+        v:SetScript('OnDragStart', nil)
+        v:SetScript('OnDragStop', nil)
+        v:EnableMouse(false)
+        v:SetMovable(false)
+
+        print('Saving bar coordinates post unlock')
+
+        db[k].x = v:GetLeft()
+        db[k].y = v:GetTop()
+        db[k].anchor = 'BOTTOMLEFT'
+        db[k].anchorTo = 'BOTTOMLEFT'
+        db[k].parent = 'UIParent'
+      end
+    end
+end
+
+--- Store spell requests for use in tracking lag time
+function mod:SpellCastRequest(e, unit, spellName, rank, target, castID)
+  print('|cFFFF4400Request sent:|r ', spellName, castID, target)
+  self.castbar.player.sent[castID] = {
+    spellName = spellName,
+    castID = castID,
+    sendTime = GetTime()*1000,
+  }
+end
+
+--- Handle events pertaining to a cast bar
+function mod:SpellCastEvent(event, unit, ...)
+  if not self.castbar[unit] then
+    return
+  end
+
+  local u = T.unit[unit]
+  local c = self.castbar[unit]
+
+  --- doubling as an invocation source test
+  local spellName, rank, castID, spellID = ...
+
+  local channelinfo = T.unit[unit].channeling
+  local castinginfo = T.unit[unit].casting
+  local sendq = c.sent
+  local castd = c.last
+  local timestamp = GetTime()*1000
+
+  --todo: remove truncation
+  local e = event:match("UNIT_SPELLCAST_([%a_]+)")
+  print(GetTime(), '|cFFFF8747'..e..(strpad(' ',12-#e))..'|r |cFFDDFF00#'..tostring(castID)..'|r', spellName, spellID)
+
+  c.channeling = channelinfo and channelinfo[1] or nil
+  c.casting    = castinginfo and castinginfo[1] or nil
+  if c.channeling then
+    -- {name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible}
+    c.startTime = channelinfo[5]
+    c.endTime = channelinfo[6]
+    c.nonInterruptible = channelinfo[8]
+  elseif c.casting then
+    --name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible
+    c.startTime = castinginfo[5]
+    c.endTime = castinginfo[6]
+    c.castID = castinginfo[8]
+    c.nonInterruptible = castinginfo[9]
+  else
+    print('  |cFFFF7700internal data was nil|r')
+  end
+
+  --- process the "event"
+  local setSpell, setState
+  if e == 'START' then
+    setSpell = true
+    setState = true
+
+    -- player data calculations
+    if unit == 'player' then
+      if sendq[castID] and sendq[castID].spellName == spellName then
+        local sent = sendq[castID]
+        print('  |cFF00AAFFmatched:|r', sent.spellName, castID)
+        c.pingTime = c.startTime - sent.sendTime
+        print('    ping:', c.pingTime)
+        for cid, cdata in pairs(sendq) do
+          if cdata.sendTime <= sent.sendTime then
+            --print('    forgetting #'..cdata.sendTime, cid, cdata.spellName)
+            sendq[cid] = nil
+          end
+        end
+
+        if castd ~= nil then
+          print('   downtime calc hit')
+          c.downTime = timestamp - castd.endTime
+          castd = nil
+        else
+          c.downTime = nil
+        end
+        c.castID = castID
+      end
+    end
+  elseif e == 'CHANNEL_START' or e == 'CHANNEL_UPDATE' then
+    setSpell = true
+    setState = true
+    c.event = e
+
+  elseif e == 'STOP' then
+    setState = true
+    c.casting = nil
+    castd = {
+      spellName = spellName,
+      spellID = spellID,
+      castID = castID,
+      endTime = timestamp,
+    }
+    print('    |cFFFFFF00found', timestamp, spellName)
+
+  elseif e == 'CHANNEL_STOP' then
+    setState = true
+    c.channeling = nil
+    castd = {
+      spellName = spellName,
+      spellID = spellID,
+      castID = castID,
+      endTime = timestamp,
+    }
+    print('    |cFFFF0088found', timestamp, spellName)
+
+  elseif e == 'SUCCEEDED'  then
+    if c.spellName == spellName and not c.channeling then
+      setState = true
+      print('      |cFFFF4400',c.spellName,'=',spellName,' pushing event', e)
+    end
+    -- if there are 'send' args when this fires, it means it's an instant cast
+    if sendq[castID] and sendq[castID].spellName == spellName then
+      castd = {
+        spellName = spellName,
+        spellID = spellID,
+        castID = castID,
+        endTime = timestamp,
+      }
+      print('    |cFFFFAA00found', timestamp, spellName)
+    end
+
+  elseif e =='FAILED' then
+    if spellName == c.spellName and not c.channeling then
+      setState = true
+    end
+
+  elseif e == 'INTERRUPTED' then
+    setState = true
+
+  end
+
+
+
+
+  if setSpell then
+    print('      |cFF44FF00setting spell', spellName)
+  end
+  if setState then
+    print('      |cFFFF4400pushing event', e)
+  end
+
+  if setSpell then
+    c:SetSpell(e)
+  end
+  if setState then
+    c:SetState(e)
+  end
+end
+
+
+--- Sets all the static elements of the casting bar, such as config values, icon texture, and name
+function mod.SetSpell(c, event)
+  local _
+  local time, alpha = GetTime(), c:GetAlpha()
+  local u, cdb  = T.unit[c.unit], c.db
+
+  c.stopped = nil
+  c.stopping = nil
+  print('New spell:', c.spellName)
+  --- use only internal info
+  local data = (event == 'CHANNEL_START' or event == 'CHANNEL_UPDATE' or event == 'CHANNEL_STOP') and u.channeling or u.casting
+  local spellName, rank, displayName, texture, startTime, endTime, isTradeSkill, castID, nonInterruptible = unpack(data)
+
+
+  if spellName == nil then
+    print("  Can't do arithmetic, no data.")
+  else
+    print(cText("  Arithmetic vars:"), cNum(startTime), cNum(endTime))
+  end
+
+         c.spellName = spellName
+            c.castID = castID or 0
+              c.rank = rank
+       c.displayName = displayName
+           c.texture = texture
+  c.nonInterruptible = nonInterruptible
+      c.isTradeSkill = isTradeSkill
+           c.endTime = endTime   or 0
+         c.startTime = startTime or 0
+          c.duration = c.endTime - c.startTime
+
+  if c.unit == 'player' then
+    if c.pingTime then
+      print('     ping',c.pingTime, endTime, startTime, c.duration)
+      local draw_dist = (c.pingTime / c.duration) * (c.fill_width)
+      if draw_dist > c.fill_width then
+        draw_dist = c.fill_width
+      end
+      print('SET AND SHOW PING ', c.pingTime,'on', c.pingbar:GetName())
+      c.pingbar:SetWidth(draw_dist)
+      c.pingbar:Show()
+
+      local rv = c.pingTime - PING_FLOOR
+      local rr = PING_MIDLINE - PING_FLOOR
+      local gv = c.pingTime   - PING_MIDLINE
+      local gr = PING_CEILING - PING_MIDLINE
+      local r = c.pingTime > PING_MIDLINE and 1 or (c.pingTime < PING_FLOOR and 0 or (rv / rr))
+      local g = c.pingTime < PING_MIDLINE and 1 or (c.pingTime > PING_CEILING and 0 or 1-(gv / gr))
+      print(c.pingTime, rv, '/', rr, '=', r)
+      print(c.pingTime, gv, '/', gr, '=', g)
+
+      c.ping:SetText(floor(c.pingTime))
+      c.ping:SetTextColor(r,g,0)
+      c.ping:Show()
+    else
+      c.pingbar:Hide()
+      c.ping:Hide()
+    end
+    if c.downTime then
+      c.downtime:Show()
+      if c.downTime > 1500 then
+        c.downtime:SetText(nil)
+      else
+        c.downtime:SetText(c.downTime)
+      end
+    else
+      c.downtime:Hide()
+    end
+  else
+    if c.nonInterruptible then
+      c.interrupt:Show()
+    else
+      c.interrupt:Hide()
+    end
+  end
+
+  c.icon:SetTexture(texture)
+  c.spelltext:Show()
+  c.spelltext:SetText(spellName)
+
+  c.fill_inverse = c.channeling
+  -- set timers
+end
+
+--- Deals with failed/succeeded/interrupted visuals and fading cues
+-- Fired by one of those events, or cast/channel info is returning nothing
+function mod:SetState(event)
+  if T.unlocked then
+    return
+  end
+
+  local time = GetTime() * 1000
+  print(' ',self:GetName(), '|cFF44FF00event trigger:|r', event)
+
+  -- We want these to be updating no matter what is happening
+  if TEXTURE_SUFFIX[event] then
+    local cdb = self.db
+    if cdb.foreground_texture then
+      print('   |cFF00AAFFevent|r '..event..', |cFF00AAFFtexture|r ', cdb.foreground_texture)
+      self.foreground:SetVertexColor(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color))
+    else
+      self.foreground:SetTexture(unpack(cdb['foreground' .. TEXTURE_SUFFIX[event]] and cdb['foreground' .. TEXTURE_SUFFIX[event]] or cdb.foreground_color))
+    end
+
+    if cdb.background_texture then
+      print('  texture=', cdb.background_texture)
+      self.background:SetVertexColor(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color))
+    else
+      self.background:SetTexture(unpack(cdb['background' .. TEXTURE_SUFFIX[event]] and cdb['background' .. TEXTURE_SUFFIX[event]] or cdb.background_color))
+    end
+  end
+
+  -- are we starting or stopping
+  if event == 'START' or event == 'CHANNEL_START' then
+    print('   |cFF00AAFFStarting display|r ', self.spellName)
+    self:Show()
+    if self.__fade:IsPlaying() then
+      self.__fade:Stop()
+    end
+
+    self:Fade(FADE_IN_TIME, self.alpha)
+  else
+    if event == 'SUCCEEDED' and not self.channeling then
+      print('   |cFF00AAFFsuccess deduced values|r', self.value, 'to', self.duration)
+      self.percent = 1
+      self.value = self.duration
+    end
+
+    -- Actual fading out begins here, anything else is statistical sugar
+    if event == 'STOP' or event == 'CHANNEL_STOP' then
+      print('yeah we done')
+      if self.__fade:IsPlaying() then
+        self.__fade:Stop()
+      end
+      self:Fade(FADE_OUT_TIME, 0)
+    end
+  end
+end
+
+--- Animation loop
+function mod:Update()
+  local time = GetTime() * 1000
+  local u = self.unit
+  local s = self
+
+  -- update vals
+  if s.casting or s.channeling then
+    self.value = s.casting and (time - s.startTime) or (s.endTime - time)
+    self.percent = (time - s.startTime) / self.duration
+    if time > s.endTime then
+      self.value = self.fill_inverse and 0 or s.duration
+      self.percent = self.fill_inverse and 0 or 1
+      self.elapsed = time - s.endTime
+    end
+    --_G.print('Update','   ',self.unit, self.value, self.percent)
+  else
+    self.value = 1000
+    self.percent = 1
+  end
+  self.casttime:SetText(format("%.2f", self.value / 1000))
+
+  self:SetProgress(self.percent)
+end
+
+--- Refresh the CastingBar for things like target change, pet despawn, etc.
+-- Will attempt to flush out any existing cast action data, then re-invoke SpellCastEvent() with as much data can be acquired.
+-- There is no event data such as spellID directly available, so this is essentially the biggest constraint we have on what
+-- the castingbar can depend on.
+function mod:UpdateUnit(unit)
+  local print = function(...) _G.print('Update', ...) end
+  print(cText 'GUID changed:|cFFFFFF99', unit)
+  local c = mod.castbar[unit]
+  if c.channeling or c.casting then
+    print(cText '  was casting a spell, run STOP event and hide completely')
+    c:SetState(c.casting and 'STOP' or 'CHANNEL_STOP')
+    c.channeling = nil
+    c.casting = nil
+    c:Hide()
+  end
+
+  if T.unit[unit].casting then
+    print(cText('  new target is CASTING'))
+    mod:SpellCastEvent('UNIT_SPELLCAST_START', unit)
+  end
+  if T.unit[unit].channeling then
+    print(cText('  new target is CHANNELING'))
+    mod:SpellCastEvent('UNIT_SPELLCAST_CHANNEL_START', unit)
+  end
+
+  T:UNIT_SPELLCAST(unit)-- name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible
+  T:UNIT_CHANNEL(unit)  -- name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible
+
+end
+
+function mod:UpdateInterrupt(e, unit, ...)
+    mod.castbar[unit].nonInterruptible = (e == 'UNIT_SPELLCAST_UNINTERRUPTIBLE') and true or false
+  if unit ~= 'player' and unit ~='pet' then
+
+  end
+end
+
+mod.UNIT_PET = function(self, e, unit)
+  if unit == 'player' then
+    self:UpdateUnit('pet')
+  end
+end
+mod.PLAYER_TARGET_CHANGED = function(self, ...)
+  _G.print('Main', cPink(self),...)
+  self:UpdateUnit('target')
+end
+mod.PLAYER_FOCUS_CHANGED = function(self)
+  self:UpdateUnit('focus')
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Castbar.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,74 @@
+<Ui>
+
+  <Frame name="TurokCastingBar" inherits="TurokStatusBar" virtual="true" hidden="true">
+    <Scripts>
+      <OnHide>
+        self:SetAlpha(0)
+        self.casttime:SetText(nil)
+        self.spelltext:SetText(nil)
+        self.casting = nil
+        self.channeling = nil
+        self.succeeded = nil
+        self.fading = nil
+        self.fadeIn = nil
+        self.fadeOut = nil
+        self.stopping = nil
+      </OnHide>
+    </Scripts>
+    <Layers>
+      <Layer level="OVERLAY">
+        <Texture name="$parentSpark" parentKey="spark" file="Interface\CastingBar\UI-CastingBar-Spark" alphaMode="ADD" hidden="true" />
+        <Texture name="$parentPingBox" parentKey="ping" hidden="true" blendMode="ADD">
+          <Color r="1" g="0.25" b="0" a="0.5" />
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" x="-3" y="3" />
+            <Anchor point="TOPRIGHT" x="-3" y="-3" />
+          </Anchors>
+        </Texture>
+
+        <!-- SPELL NAME -->
+        <FontString name="$parentSpellText" parentKey="spelltext" inherits="TurokFont" height="18" outline="NORMAL">
+          <Anchors>
+            <Anchor point="LEFT" relativePoint="LEFT" relativeKey="foreground" x="6" y="0"/>
+          </Anchors>
+        </FontString>
+
+        <!-- CAST TIME -->
+        <FontString name="$parentTimeText" parentKey="casttime" inherits="TurokFont" height="18" outline="NORMAL">
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="RIGHT" relativePoint="RIGHT" relativeTo="$parent" x="-6" y="0" />
+          </Anchors>
+        </FontString>
+
+        <!-- CAST LAG -->
+        <Texture name="$parentPingBar" parentKey="pingbar" alphaMode="ADD" hidden="true">
+          <Color r="1" g="0" b="0" a="0.7" />
+        </Texture>
+
+        <FontString name="$parentPingText" parentKey="ping" inherits="TurokFontDetail" hidden="true">
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMLEFT" outline="NORMAL" relativeTo="$parent" x="-6" y="-3" />
+          </Anchors>
+        </FontString>
+
+        <!-- CHAINCAST LAG -->
+        <FontString name="$parentDownTimeText" parentKey="downtime" inherits="TurokFontDetail" hidden="true">
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" outline="NORMAL" relativeTo="$parent" x="6" y="-3" />
+          </Anchors>
+        </FontString>
+
+        <!-- NOT-INTERRUPTIBLE -->
+        <Texture file="Interface/CastingBar/UI-CastingBar-Arena-Shield" name="$parentInterruptStatus" parentKey="interrupt" hidden="true">
+          <Size x="94" y="94" />
+          <Anchors>
+            <Anchor point="CENTER" relativePoint="RIGHT" relativeKey="$parent.icon" x="0" y="0" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+  </Frame>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Combat.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,10 @@
+<Ui>
+  <Include file="Castbar.xml" />
+  <Include file="CombatLog.xml" />
+  <Script file="Castbar.Init.lua" />
+  <Script file="Castbar.lua" />
+  <Script file="CombatLog.lua" />
+  <Include file="Powerbar.xml" />
+  <Script file="Powerbar.Init.lua" />
+  <Script file="Powerbar.lua" />
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/CombatLog.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,758 @@
+--- Combat
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/11/2016 4:27 PM
+--- Data collector for API Combat Log Event
+local T, _G = Turok, _G
+local mod = T:NewModule("Combat")
+local pairs, ipairs, select, concat, format, tinsert, wipe = pairs, ipairs, select, table.concat, string.format, tinsert, table.wipe
+local GetSpellTexture, CreateFrame, GetTime, unpack, floor = GetSpellTexture, CreateFrame, GetTime, unpack, floor
+local db
+local cText, cNum, cKey, cWord, cPink, cType, cBool = cText, cNum, cKey, cWord, cPink, cType, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('CombatText', ...)
+  end
+end
+
+
+--- performance constants
+local SCT_NAME = 'TurokSCT%s'
+local SCT_TEMPLATE = 'TurokSCTAnchorTemplate'
+local SCT_PREGAME = 5
+local SCT_MESSAGE_TEMPLATE = 'TurokCombatMessageTemplate'
+local SCT_MESSAGE_NAME = 'CombatString%d'
+local LOG_TEMPLATE =  'TurokCombatLogAnchorTemplate'
+
+--- UX structures
+T.defaults.CombatText = {
+  width = 400, height = 800,
+   parent = 'UIParent',
+  Outgoing = {
+    anchor = 'RIGHT', anchorTo = 'RIGHT',
+    x = -400, y = -200,
+  },
+  Incoming = {
+    anchor = 'LEFT', anchorTo = 'LEFT',parent = 'UIParent',
+    x = 400, y = -200,
+  },
+  DoTTracker = {
+    anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOM', parent = 'UIParent',
+    x = 80, y = 400,
+  },
+  defaultFont = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf", 18, 'OUTLINE'},
+  defaultAnimation = 'slide',
+
+  textFonts = {
+    font1 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 30, 'OUTLINE'},
+    font2 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Regular.ttf" , 24, 'OUTLINE'},
+    font3 = {"Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Bold.ttf" , 20, 'OUTLINE'},
+  },
+
+  --- [(string) combatEvent] = {[1] = (string) format [, [2] = fontKey]}
+  --- Substitution values
+  -- %d [1] amount [2] overkill [3] absorbed [4] blocked
+  -- %s [5] spell [6] caster [7] school
+  textFormat = {
+    ['SWING_DAMAGE'] = {'%d'},
+    ['SPELL_DAMAGE'] = {'%d'},
+    ['RANGE_DAMAGE'] = {'%d'},
+    Incoming = {
+      ['SWING_DAMAGE'] = {'-%d'},
+      ['SPELL_DAMAGE'] = {'-%d (%s)'},
+      ['RANGE_DAMAGE'] = {'-%d'},
+    },
+  },
+  textModifiers ={
+    critical = {'%s!', 'pop'},
+    overKill = {'%s |cFF0088FFKilling Blow!|r', 'slide'},
+    multistrike = {'<%s>', 'lateralSlide', 'font3'},
+    absorbed = {'%s (%d)', 'slide'},
+    blocked = {'%s {%d}', 'slide'},
+    pet = {'(%s)', 'lateralSlide'},
+    grouped = {'%s (%d hit)', 'slide'},
+  },
+  --- [AnimationGroup key] = {[XML attrib] = [value], d[x/y] = (number) 0-1 }
+   -- d[x/y] indicates the proportional relevance of each FontString dimension when frames are displaced by a new event
+   -- x/y    indicates the ranges of movement made by an animation, and are also considered when calculating displacement
+  animation = {
+    slide = {
+      x = 0,   dx = 0,
+      y = 300, dy = 1,
+      duration = 2
+    },
+    lateralSlide = {
+      x = 300, dx = 1,
+      y = 0,   dy = 0,
+      duration = 2
+    },
+    pop = {
+      toScale = 1.4,
+      fromScale = 0.1,
+      duration = 0.14,
+      dx = 0, dy = 1,
+    },
+    fadeOut = {change = -1, duration = 0.5},
+  },
+}
+--- [1] text wrapper [2] animation type
+local dotEvents = {
+  SPELL_AURA_APPLIED = true,
+  SPELL_PERIODIC_DAMAGE = true,
+  SPELL_AURA_REMOVED = true,
+  SPELL_AURA_REFRESHED = true,
+}
+local petGUID = {}
+
+------------- data structures
+local defaultAnimation, defaultFont
+local criticalModifier, absorbedModifier, blockedModifier, overKillModifier, multistrikeModifier, groupedModifier
+local criticalAnimation, absorbedAnimation, blockedAnimation, overKillAnimation, multistrikeAnimation, groupedAnimation
+local criticalFont, absorbedFont, blockedFont, overKillFont,multistrikeFont, groupedFont
+local textFonts = {}
+local textFormat = {}
+local animation = {}
+local spellCache = {}
+local DoTFrames = {}
+
+local dotTrackingEvents = {
+  ['SPELL_AURA_APPLIED'] = true,
+  ['SPELL_AURA_REMOVED'] = true,
+  ['SPELL_AURA_REFRESHED'] = true,
+}
+
+--- multi-hit events data
+-- [(string) localized name] = {
+--   [1] = (bool)   controlled channel
+--   [2] = (number) minimum time to wait for next damage tick
+--   [3] = (number) maximum time to log grouped spell hits
+--   [4] = (string) combatEvent full string
+--   [5] = (string) combatEvent short prefix (as opposed to SPELL_PERIODIC)
+--   [6] = (string) combatEvent short suffix (as opposed to AURA_APPLIED)
+-- }
+-- tailing fields are set to reduce the number of arguments passed around
+local groupedSpells = {
+  global = {false, 1, 'SPELL_PERIODIC_DAMAGE', 'SPELL', 'DAMAGE'},
+  ['Crimson Tempest'] = {false,   0.3,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave dot
+  ['Mind Sear'] =       {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
+  ['Searing Insanity'] =       {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
+  ['Ice Nova'] =        {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe
+  ['Blizzard'] =        {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- scanning aoe
+  ['Frozen Orb'] =      {false,   1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'},
+  ['Ice Bomb'] =        {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- shockwave aoe
+  ['Comet Storm'] =     {false, 2.5,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- staggered multi-hit
+  ['Barrage'] =         {false, 1.1,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- projectile vommit
+  ['Glaive Toss'] =     {false,   3,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}, -- path projectile,
+  ['Doom Nova'] =       {false, 0.4,  'SPELL_DAMAGE', 'SPELL', 'DAMAGE'}
+}
+
+local dotSpellIndex = {
+  ['Shadow Word: Pain'] = {'HARMFUL|PLAYER'},
+  ['Vampiric Touch'] = {'HARMFUL|PLAYER'}
+}
+
+local offhandSpellIndex = {
+  ['Execute'] = {'Execute Off-Hand'},
+  ['Mutilate'] = {'Mutilate Off-Hand'},
+  ['Stormstrike'] = {'Stormstrike Off-Hand'},
+}
+
+--- stored as a list of field representations to be concatenated together within a loadstring def
+
+
+
+--- Tracking tables
+local groupedQueue = {}
+local groupedEvents = {}
+local offhandQueue = {}
+local sct_format = {}
+
+
+local function GetGUIDInfo(guid)
+  local unitType, flags1, flags2 = guid:match('(%a+)\-(%x+)\-(%x+)')
+  return concat({unitType, flags1, flags2},'|r::|cFF0088FF')..'|r'
+end
+
+local SpellSchoolColors = {
+  [1] = {255, 255,0},     -- physical
+  [2] = {255, 230, 128},  -- holy
+  [3] = {255, 255, 128},  -- holy+phys
+  [4] = {255, 128, 0},    -- fire
+  [8] = {77, 255, 77},    -- nature
+  [16] = {128, 255, 255}, -- frost
+  [20] = {255, 192, 128},   -- frostfire
+  [32] = {128, 128, 255}, -- shadow
+  [48] = {128, 192, 255}, -- shadow+frost
+  [64] = {255, 128, 255}, -- arcane,
+  [72] = {255, 128, 128}, -- spellstorm,
+}
+
+local h = '|cFF%02X%02X%02X'
+local SpellSchoolColor = function (flags, spellName)
+  local i = 64
+  local rA, gA, bA
+  if SpellSchoolColors[flags] then
+    print(flags, 'match')
+    print(format('%02X%02X%02X', unpack(SpellSchoolColors[flags])))
+    return format(h, unpack(SpellSchoolColors[flags]))
+  end
+
+  repeat
+    local rB, gB, bB = unpack(SpellSchoolColors[i])
+    if i <= flags then
+      if not rA then
+        rA = rB
+        gA = gB
+        bA = bB
+      else
+        rA = (rA+rB)/2
+        gA = (gA+gB)/2
+        bA = (bA+bB)/2
+      end
+      print('test:', cWord(i), '<=', cKey(flags), '=', (i <= flags), cPink(rA), cNum(gA), cText(bA))
+      flags = flags - i
+    else
+      print(i, 'skip')
+    end
+    i = i/2
+  until (i == 1)
+  SpellSchoolColors[flags] = {rA, gA, bA }
+  --print(string.format('%02X%02X%02X', unpack(SpellSchoolColors[flags])))
+  return format(h, unpack(SpellSchoolColors[flags]))
+end
+local myGUID
+
+mod.NewCombatMessage = function(frame, index)
+  local ct = CreateFrame('Frame', SCT_MESSAGE_NAME:format(frame.lineID), frame, SCT_MESSAGE_TEMPLATE)
+  index = index and 'active' or 'expired'
+  frame.lineID = frame.lineID + 1
+  frame.active[#frame.active+1] = ct
+  ct.index = #frame.active
+  ct.x = 0
+  ct.y = 0
+  ct.point = 'BOTTOMLEFT'
+  return ct
+end
+
+--- frame interaction logic
+mod.AddCombatMessage = function(frame, text, icon, animationType, fontKey)
+  local line
+  print(fontKey)
+
+  local expired = frame.expired
+  local active = frame.active
+  local a = 1
+  local shiftY, shiftX
+  local lastFrame
+  --- If animation has overlap delta values, find the last active frame of that animation type and check for overlaps.
+  --- Frames are considered overlapping when the last member's ((height - distance traveled) * delta) is over 0.
+  --- This assumes the same string height for the upcoming frame since wordwrap isn't enabled.
+  print(animationType)
+  if animation[animationType].dx or animation[animationType].dy then
+    print('animation has displacement')
+    if frame.last then
+      lastFrame = frame.last
+      local dp = lastFrame[animationType]:GetProgress()
+      if animation[animationType].dx then
+        local dx = dp * animation[animationType].x
+        shiftX = (lastFrame.string:GetStringWidth() - dx) * animation[animationType].dx
+      end
+      if animation[animationType].dy then
+        local dy = dp * animation[animationType].y
+        shiftY = (lastFrame.string:GetStringHeight() - dy) * animation[animationType].dy
+        print('  ', 'h=', floor(lastFrame.string:GetStringHeight()), 'dY=', dy, 'offsetY=', shiftY)
+      end
+      print(cWord('lastFrame hit:'), lastFrame and lastFrame:GetName(), cNum(shiftX), cNum(shiftY))
+    end
+  end
+
+  -- find a usable frame
+  local currentFrame
+  for i, ct in ipairs(frame.active) do
+    if not currentFrame then
+      if ct.discard then
+        ct.discard=  nil
+        currentFrame = ct
+      end
+    end
+
+    if lastFrame and ct.animationType == animationType and not ct.discard then
+      --print('lastFrame defined, check for overlap')
+      if shiftY > 0 then
+        print(cWord('  * vertical shift'), cNum('+'..floor(shiftY)))
+        ct.y = ct.y + shiftY
+      end
+      if shiftX > 0 then
+        print(cWord('  * horizontal shift'), cNum('+'..floor(shiftX)))
+        ct.x = ct.x + shiftX
+      end
+      ct:SetPoint(ct.point, frame, ct.point, ct.x, ct.y)
+    end
+  end
+  -- if no expired frames became available, make a new one (should max at 20 or so if groupings are right)
+  if  not currentFrame then
+    currentFrame = mod.NewCombatMessage(frame)
+    print(cNum('     creating new string object for the heap'))
+  end
+
+  print(cText(' * Starting'), cPink(animationType), 'on', cKey(currentFrame:GetName()), ' ['..cNum(currentFrame.index)..']')
+
+  if icon then
+    currentFrame.icon:Show()
+    currentFrame.icon:SetTexture(icon)
+  else
+    currentFrame.icon:Hide()
+  end
+  if fontKey  then
+    local newFont = fontKey and textFonts[fontKey] or defaultFont
+
+    local path, size, flags = currentFrame.string:GetFont()
+    path = newFont[1] or path
+    size = newFont[2] or size
+    flags = newFont[3] or flags
+    print(cText('font ('..cWord(fontKey)..'):'), path, size, flags)
+    local result = currentFrame.string:SetFont(path, size, flags)
+    print(cNum('     result:'), cNum(result), currentFrame.string:GetFont())
+    --currentFrame.fontKey = fontKey
+  end
+
+  currentFrame.animationType = animationType
+  currentFrame.string:SetText(text)
+  local cHeight = currentFrame.string:GetStringHeight()
+  currentFrame:SetSize(currentFrame.string:GetStringWidth(), cHeight)
+  currentFrame.icon:SetSize(cHeight, cHeight)
+  currentFrame.y = 0
+  currentFrame.x = 0
+  currentFrame:SetPoint(currentFrame.point, frame, currentFrame.point, currentFrame.x, currentFrame.y)
+  currentFrame[animationType]:Play()
+  frame.last = currentFrame
+end
+
+local GetTextFormatFunc = function(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName)
+  local sub_text = {
+    ['%d'] = amount,
+    ['%o'] = overKill,
+    ['%a'] = absorbed,
+    ['%b'] = blocked,
+    ['%m'] = multistrike,
+    ['%s'] = spellName,
+    ['%n'] = sourceName,
+    ['%t'] = destName,
+  }
+  local func = function(token)
+    print(cPink('gsub run:'), token)
+    return sub_text[token]
+  end
+  return func
+end
+
+--- builds CT messages from combat log event data
+--  separated from frame interaction for auxiliary events such as combat/loot/etc
+local CreateDamageText = function(frame, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike, ticks)
+  if combatEvent:match('^SWING') then
+    spellID = 1
+    spellName = 'Attack'
+  elseif combatEvent:match('^RANGE') then
+    spellID = 2
+    spellName = 'Auto Shot'
+  end
+  if not combatEvent:match('DAMAGE') then
+    return
+  end
+
+
+  local fontKey = 'font1'
+  local icon = GetSpellTexture(spellID)
+  local animationType = defaultAnimation
+  local text = amount
+  if textFormat[combatEvent] then
+    local tString, fontKey = unpack(textFormat[combatEvent])
+    if amount > 1000000 then
+      amount = (floor(amount/100000)/10) ..'M'
+    elseif amount > 1000 then
+      amount = (floor(amount/100)/10) ..'k'
+    end
+
+    text = tString:gsub('%%[doabsnt]', GetTextFormatFunc(amount, overKill, absorbed, blocked, multistrike, spellName, sourceName, destName))
+    print("** font override:", '"'..cText(tString)..'",', cPink(font), cNum(size), cWord(outline))
+  end
+
+  print('** getting color data', cText(spellName))
+  if type(effectSchool) == 'number' then
+    text = SpellSchoolColor(effectSchool) ..text..'|r'
+  end
+
+  if overKill > 0 then
+    text = overKillModifier:format(text, overKill)
+    animationType = overKillAnimation
+    fontKey = overKillFont or fontKey
+  end
+  if critical then
+    text = criticalModifier:format(text)
+    animationType = criticalAnimation
+    fontKey = criticalFont or fontKey
+  end
+  if absorbed then
+    text = absorbedModifier:format(text, absorbed)
+    animationType = absorbedAnimation
+    fontKey = absorbedFont or fontKey
+  end
+  if blocked then
+    text = blockedModifier:format(text, blocked)
+    animationType = blockedAnimation
+    fontKey = blockedFont or fontKey
+  end
+  if multistrike then
+    text = multistrikeModifier:format(text, multistrike)
+    animationType = multistrikeAnimation
+    fontKey = multistrikeFont or fontKey
+  end
+
+  print(ticks)
+  if ticks then
+    text = groupedModifier:format(text, ticks)
+    animationType = groupedAnimation
+    fontKey = groupedFont or fontKey
+  end
+
+
+  print('** sending format to SCT:', text, icon, animationType, fontKey)
+
+
+  mod.AddCombatMessage(frame, text .. '|r', icon, animationType, fontKey)
+end
+
+local CT_ShowConsolidated = function()
+  for spellName, queuedSpell in pairs(groupedQueue) do
+    local isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, SCT, timestamp = unpack(queuedSpell)
+    for i, v  in ipairs(queuedSpell) do
+      print('   ', i, '=', v)
+    end
+
+    print(spellName, 'vars:', spellID, spellName, school, SCT, timestamp)
+    if groupedEvents[spellName] and #groupedEvents[spellName] ~= 0 then
+      local amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit = 0, -1, 0, 0, 0, 0, 0, 0, 0, 0
+      for i, line in ipairs(groupedEvents[spellName]) do
+        -- extra strike?
+        --{amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike}
+        local amount_n, overKill_n, resisted_n, blocked_n, absorbed_n, critical_n, glancing_n, crushing_n, multi_n, sourceGUID, destGUID, sourceName, destName = unpack (line)
+
+        amount = amount + amount_n
+        if overKill_n > 0   then overKill = overKill + overKill_n end
+        if blocked_n        then blocked = blocked + line[4] end
+        if absorbed_n       then absorbed = absorbed + line[5] end
+        if critical_n       then critical = critical + 1 end
+        if multi_n          then multistrike = multistrike + 1  end
+
+        hit = hit + 1
+      end
+      if overKill == -1 then
+        overKill = overKill + 1
+      end
+      wipe(groupedEvents[spellName])
+      groupedQueue[spellName] = nil
+      print('   expelling', spellName, cText('A:'), cNum(amount), cText('O:'), cNum(overKill), cText('Ticks:'), cNum(hit), cText('Crits:'), cNum(critical), cText(school))
+      print(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, hit)
+      CreateDamageText(SCT, timestamp, combatEvent, sourceName, destName, spellID, spellName, school, amount, -1, nil, nil, nil, false, glancing, crushing, false, multistrike, hit)
+    end
+  end
+end
+
+local CT_ConsolidateText = function (self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, school, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
+  local archive = groupedSpells[spellName] and groupedSpells[spellName] or groupedSpells.global
+  local isChannel, minDelay, combatEvent = unpack(archive)
+  if not groupedEvents[spellName] then
+    groupedEvents[spellName] = {}
+  end
+
+  if #groupedEvents[spellName] == 0 then
+    groupedQueue[spellName] = {isChannel, minDelay, combatEvent, sourceName, destName, spellID, school, self, timestamp}
+    T:ScheduleTimer(CT_ShowConsolidated, minDelay)
+    print('  starting archive for', select(7, unpack(archive)))
+  else
+    print('  recording into archive', cText(spellName))
+  end
+  tinsert(groupedEvents[spellName], {amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, multistrike, sourceGUID, destGUID, sourceName, destName, sourceName, destName,})
+end
+
+local CT_OnCast = function(self, event, unit, spellName, lineID, spellID)
+  if unit ~= 'player' and unit ~= 'pet' then
+    return
+  end
+
+  if groupedSpells[spellName] then
+    print(cText('** Spell casting info received'))
+    local isChannel, delay = unpack(groupedSpells[spellName])
+    if isChannel and event == 'CHANNEL_STOP' and groupedEvents[spellName] then
+      T:ScheduleTimer(CT_ShowConsolidated, delay)
+    else
+      if not groupedEvents[spellName] then
+        groupedEvents[spellName] = {}
+      end
+    end
+  end
+end
+
+local CT_Unlock = function(str)
+  for i, CT in pairs(mod.CombatTextFrames) do
+      local frame = CT.frame
+      frame.configMode = (frame.configMode == nil) and true or nil
+      frame:RegisterForDrag(frame.configMode and 'LeftButton' or nil)
+      frame:EnableMouse(frame.configMode and true or false)
+      print(i, frame.configMode and 'ON' or 'OFF')
+      for _, reg in ipairs(frame.configRegions) do
+        if frame.configMode then
+          reg:Show()
+        else
+          reg:Hide()
+        end
+      end
+
+      if frame.configMode then
+        CT.configTimer = T:ScheduleRepeatingTimer(function()
+          print(i, 'config tick')
+          for i, s in ipairs(CT.sample) do
+            CT.OnEvent(frame, unpack(s))
+          end
+        end, 2)
+      else
+        print(CT.configTimer)
+        T:CancelTimer(CT.configTimer)
+      end
+
+  end
+end
+
+--- check for sectors
+local queue = {}
+mod.OnDamage = function(self, event, ...)
+  local isVisible
+
+  local timestamp, combatEvent = ...
+  print(cText('* CT'), cKey(combatEvent))
+  if combatEvent == 'UNIT_DIED' then
+    return
+  end
+  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, spellSchool = select(4, ...)
+  print('  from', cPink(sourceGUID), 'spell', cNum(spellID), cText(spellName), '->', cKey(destGUID))
+
+  -- SWING starts at arg 10, SPELL/RANGE start at arg 13, ENVIRONMENTAL starts at arg 8
+  local offset = 15
+  print(combatEvent:sub(0,3))
+  if combatEvent:sub(0,3) == 'SWI' then
+    offset = 10
+  elseif combatEvent:sub(0,3) == 'ENV' then
+    offset = 8
+  end
+  local amount, overKill, effectSchool, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(offset, ...)
+
+  print('  dmg', amount, overKill)
+
+  local sc = SpellSchoolColors[effectSchool]
+  if groupedSpells[spellName] then
+    print('* ', cText(spellName), 'to consolidator')
+    CT_ConsolidateText(self, timestamp, sourceGUID, destGUID, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
+    return
+  end
+
+  print('displaying on', cWord(self:GetName()))
+  CreateDamageText(self, timestamp, combatEvent, sourceName, destName, spellID, spellName, effectSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike)
+end
+
+mod.maxDotFrame = 1
+mod.OnDotEvent = function (self, event, timestamp, combatEvent, ...)
+  print(cWord('DOT'), combatEvent)
+  local sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, destExtraFlags, spellID, spellName, castSchool, amount, overKill, resisted, blocked, absorbed, critical, glancing, crushing, isOffHand, multistrike = select(2, ...)
+  print(cText('dot from'), cWord(sourceGUID), 'to', cKey(destGUID))
+  if sourceGUID == T.GUID then
+    local p = mod.PeriodicTable
+    p[destGUID] = p[destGUID] or {}
+    local unit = p[destGUID]
+    if not p[spellID] then
+      if not p.frames[#p.frames] then
+        unit[spellID] = CreateFrame('Frame', 'DotBar'..mod.maxDotFrame, p, 'TkDotBarTemplate')
+        mod.maxDotFrame = mod.maxDotFrame + 1
+        unit[spellID].dotID = mod.maxDotFrame
+
+        print('  create new frame ['..cKey(unit[spellID]:GetID(), '] for'), cWord(spellName), cKey(destGUID))
+      else
+        unit[spellID] = p.frames[#p.frames]
+        p.frames[#p.frames] = nil -- remove that entry
+        print('  recycling frame ['..cKey(unit[spellID].dotID, '] for'), cWord(spellName), cKey(destGUID))
+      end
+
+      unit[spellID]:Show()
+    end
+    local dot = unit[spellID]
+    local time = GetTime()
+  end
+end
+
+
+
+
+function mod:OnInitialize()
+  print('This is a thing.')
+  self.UNIT_SPELLCAST_SUCCEEDED = CT_OnCast
+  self.UNIT_SPELLCAST_CHANNEL_START = CT_OnCast
+  self.UNIT_SPELLCAST_CHANNEL_STOP = CT_OnCast
+end
+
+function mod:OnInitialize()
+  mod.db = TurokData.CombatText
+  db = TurokData.CombatText
+  myGUID = T.GUID
+  mod.PeriodicTable = mod.PeriodicTable or CreateFrame('Frame', 'TurokPeriodicFrame', UIParent)
+  mod.PeriodicTable.frames = {}
+
+  local m = mod.db.textModifiers
+  --- These values are going to be looked up a lot, so cache as close as possible
+  criticalModifier = m.critical[1] or '%s CRIT'
+  criticalAnimation = m.critical[2] or 'slide'
+  criticalFont = m.critical[3]
+
+  absorbedModifier = m.absorbed[1] or '%s ABS'
+  absorbedAnimation = m.absorbed[2] or 'slide'
+  absorbedFont = m.absorbed[3]
+
+  blockedModifier = m.blocked[1] or '%s BLK'
+  blockedAnimation = m.blocked[2] or 'slide'
+  blockedFont = m.blocked[3]
+
+  overKillModifier = m.overKill[1] or '%s KILL'
+  overKillAnimation = m.overKill[2] or 'slide'
+  overKillFont = m.overKill[3]
+
+  multistrikeModifier = m.multistrike[1] or '%s MS'
+  multistrikeAnimation = m.multistrike[2] or 'slide'
+  multistrikeFont = m.multistrike[3]
+
+  groupedModifier = m.grouped[1] or '%s'
+  groupedAnimation = m.grouped[2] or 'slide'
+  groupedFont = m.grouped[3]
+
+  --- Same as above, but for specific table values, key is determined by the combat event
+  defaultAnimation = mod.db.defaultAnimation
+  defaultFont = mod.db.defaultFont
+  for k,v in pairs(mod.db.textFormat) do
+    textFormat[k] = {v[1], v[2]}
+    print('imported textFormat.'..k, cText(textFormat[k][1]), cNum(textFormat[k][2]))
+  end
+  for k,v in pairs(mod.db.textFonts) do
+    textFonts[k] = {v[1] or defaultFont[1], v[2] or defaultFont[2], v[3] or defaultFont[3]}
+    print('imported font.'..k, cText(textFonts[k][1]), cNum(textFonts[k][2]), cWord(textFonts[k][3]))
+  end
+
+  for k,v in pairs(mod.db.animation) do
+    animation[k] = {}
+    animation[k].x = v.x
+    animation[k].y = v.y
+    animation[k].dx = v.dx
+    animation[k].dy = v.dy
+    animation[k].fromScale = v.fromScale
+    animation[k].toScale = v.toScale
+    animation[k].deviation = v.deviation
+    animation[k].change = v.change
+    animation[k].fromAlpha = v.fromAlpha
+    animation[k].toAlpha = v.toAlpha
+    animation[k].duration = v.duration
+  end
+end
+
+function mod:OnEnable()
+  T:RegisterChatCommand('tkc', CT_Unlock)
+
+  --- Populate CT frames
+  for name, CT in pairs(mod.CombatTextFrames) do
+    print('create CT', name)
+    -- make frame
+    CT.frame = CT.frame or CreateFrame('Frame', SCT_NAME:format(name), UIParent, SCT_TEMPLATE)
+
+    -- local vars
+    local db = db[name] or db
+    local frame = CT.frame
+
+    -- script defs
+    frame.IsFrameEvent = CT.trigger
+    frame.name = name
+    frame.lineID = 0
+    frame.expired = {}
+    frame.active = {}
+
+    -- frame defs
+    frame:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y)
+    frame:RegisterEvent('COMBAT_LOG_EVENT_UNFILTERED')
+    print('bound ', name, 'with', CT.OnEvent)
+    db.sample = {}
+    local logged = {}
+    frame:SetScript('OnEvent', function(self, e,...)
+      if CT.trigger(e, ...) then
+        print('event trigger fired', name)
+          if #db.sample < 5 and not logged[select(2,...)] then
+            logged[select(2,...)] = true
+            tinsert(db.sample, {e, ...})
+          end
+
+        CT.OnEvent(frame, e, ...)
+      end
+    end)
+
+    -- configurators
+    frame.configHeader:SetText(name)
+    frame:EnableMouse(false)
+
+    -- pre-pop some text frames
+    for i = (CT.lineID or 1), SCT_PREGAME do
+      print(frame:GetName(), 'pre-pop #'..i)
+      mod.NewCombatMessage(frame)
+    end
+  end
+end
+
+
+local damageEvents = {
+  ['SPELL_DAMAGE'] = true,
+  ['SPELL_PERIODIC_DAMAGE'] = true,
+  ['SWING_DAMAGE'] = true,
+  ['RANGE_DAMAGE'] = true,
+  ['SPELL_HEAL'] = true,
+}
+mod.CombatTextFrames = {
+  Incoming = {
+    trigger = function(e, _, c, _, _, _, _, _, d) return (d == T.GUID and damageEvents[c]) end,
+    OnEvent = mod.OnDamage,
+    sample = {
+      {"COMBAT_LOG_EVENT_UNFILTERED", 1455887795.271, "SPELL_HEAL", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Player-3684-07235A4E", "Klakyn", 1297, 0, 143924, "Leech", 1, 93, 93, 0, false, false, },
+    }
+  },
+  Outgoing = {
+    trigger = function(e, _,c,_, s) return (s == T.GUID and damageEvents[c]) end,
+    OnEvent = mod.OnDamage,
+    sample = {
+    {
+        "COMBAT_LOG_EVENT_UNFILTERED", 1455887795.006, "SPELL_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, }, {
+        "COMBAT_LOG_EVENT_UNFILTERED", 1455887795.271, "SPELL_HEAL", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Player-3684-07235A4E", "Klakyn", 1297, 0, 143924, "Leech", 1, 93, 93, 0, false, false, }, {
+        "COMBAT_LOG_EVENT_UNFILTERED", 1455887797.377, "SPELL_PERIODIC_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, }, {
+        "COMBAT_LOG_EVENT_UNFILTERED", 1455887812.702, "SWING_DAMAGE", false, "Player-3684-07235A4E", "Klakyn", 1297, 0, "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0, 279, -1, 1, nil, 120, nil, false, false, false, false, false, }, -- [4]
+    }
+  },
+  DoTTracker = {
+    trigger = function(e, _, c, _, s) return (s == T.GUID and dotEvents[c]) end,
+    OnEvent = mod.OnDotEvent,
+    sample = {
+      {"COMBAT_LOG_EVENT_UNFILTERED", 1455887795.006, "SPELL_AURA_APPLIED",    false,
+        "Player-3684-07235A4E", "Klakyn", 1297, 0,
+        "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0,
+        589, "Shadow Word: Pain", 32, "DEBUFF", },
+      {"COMBAT_LOG_EVENT_UNFILTERED", 1455887797.377, "SPELL_PERIODIC_DAMAGE", false,
+        "Player-3684-07235A4E", "Klakyn", 1297, 0,
+        "Creature-0-3684-1116-7-87761-0000C32119", "Dungeoneer's Training Dummy", 68136, 0,
+        589, "Shadow Word: Pain", 32, 3944, -1, 32, nil, nil, nil, false, false, false, false, false, },
+      {"COMBAT_LOG_EVENT_UNFILTERED", 1455887807.199, "SPELL_AURA_REMOVED",    false,
+        "Player-3684-07235A4E", "Klakyn", 1297, 0,
+        "Player-3684-07235A4E", "Klakyn", 1297, 0,
+        15473, "Shadowform", 32, "BUFF", },
+    }
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/CombatLog.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,186 @@
+<Ui>
+  <Frame name="TurokCombatMessageTemplate" virtual="true">
+    <Anchors>
+      <Anchor point="BOTTOMLEFT" />
+    </Anchors>
+    <Layers>
+      <Layer level="OVERLAY">
+        <Texture parentKey="icon" hidden="true">
+          <Size x="28" y="28" />
+          <Anchors>
+            <Anchor point="RIGHT" relativePoint="LEFT" x="-1" y="0" />
+          </Anchors>
+          <TexCoords top="0.15" left="0.15" right=".85" bottom="0.85" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <Font name="$parentTkMessageFont" parentKey="font" font="InterfaceAddons\Turok\Media\font\ArchivoNarrow-Regular.ttf" >
+          <FontHeight>
+            <AbsValue val="12" />
+          </FontHeight>
+        </Font>
+        <FontString name="$parentMessageText" parentKey="string" inherits="$parentTkMessageFont">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+    <Animations>
+      <AnimationGroup name="pop" parentKey="pop" ignoreFramerateThrottle="true">
+        <Scale parentArray="scale" toScaleX="1" fromScaleX="1.75" toScaleY="1" fromScaleY="1.75" duration="0.12" order="1"/>
+        <Translation parentArray="translation" offsetX="85" offsetY="70" order="1" duration="0.32" />
+        <Alpha parentArray="alpha" duration="0.75" order="1"  change="-1" startDelay="2.25"/>
+        <Scripts>
+          <OnPlay>
+            self:GetParent():Show()
+          </OnPlay>
+          <OnStop>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnStop>
+          <OnFinished>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup name="$parent_FadeAway" parentKey="slide" ignoreFramerateThrottle="true">
+        <Translation parentArray="translation" offsetX="0" offsetY="300" duration="3" order="1" />
+        <Alpha parentArray="alpha" change="-1" startDelay="2.25" duration="0.75" order="1" />
+        <Scripts>
+          <OnPlay>
+            self:GetParent():Show()
+          </OnPlay>
+          <OnStop>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnStop>
+          <OnFinished>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup name="$parent_SideAway" parentKey="lateralSlide" ignoreFramerateThrottle="true">
+        <Translation parentArray="translation" offsetX="300" offsetY="0" duration="3" order="1" />
+        <Alpha parentArray="alpha" change="-1" startDelay="2.25" duration="0.75" order="1" />
+        <Scripts>
+          <OnPlay>
+            self:GetParent():Show()
+          </OnPlay>
+          <OnStop>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnStop>
+          <OnFinished>
+            local ct = self:GetParent()
+            ct.discard = true
+            ct:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+    </Animations>
+  </Frame>
+
+  <Frame name="TurokSCTAnchorTemplate" parent="UIParent" virtual="true" movable="true" resizable="true">
+    <ResizeBounds>
+      <minResize x="40" y="40" />
+      <maxResize x="1900" y="1200" />
+    </ResizeBounds>
+    <Scripts>
+      <OnLoad>
+      </OnLoad>
+      <OnMouseDown>
+        if button ~= 'LeftButton' then
+          return
+        end
+
+        print(self.sizer:GetCenter(), self.sizer:IsMouseOver())
+        if self.sizer:IsMouseOver() then
+          self:StartSizing()
+        else
+          self:StartMoving()
+        end
+        self:SetScript('OnUpdate', function()
+          self.configHeader:SetText(self.name .."\n"
+              ..math.floor(self:GetWidth())..' x '..math.floor(self:GetHeight()).."\n("
+              ..math.floor(self:GetLeft())..', '.. math.floor(self:GetTop())..')')
+          print('hammer')
+        end)
+      </OnMouseDown>
+      <OnMouseUp>
+        if button ~= 'LeftButton' then
+          return
+        end
+        self:SetScript('OnUpdate', nil)
+        print('stop')
+        self:StopMovingOrSizing()
+      </OnMouseUp>
+    </Scripts>
+    <Anchors>
+      <Anchor point="CENTER" relativeTo="UIParent" />
+    </Anchors>
+    <Size x="400" y="500" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture setAllPoints="true" parentArray="configRegions" hidden="true">
+          <Color r="0" g="0" b="0" a="1" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString parentArray="configRegions" parentKey="configHeader" inherits="TurokFontDetail" text="configHeader" hidden="true">
+          <Anchors>
+            <Anchor point="CENTER" />
+          </Anchors>
+        </FontString>
+      </Layer>
+      <Layer level="HIGHLIGHT">
+        <Texture parentArray="configRegions" parentKey="sizer" hidden="true">
+          <Size x="24" y="24" />
+          <Color r="1" g="1" b="1" a="1" />
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="TkDotBarTemplate" virtual="true">
+    <Size x="120" y="16" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture name="$parentBackground" parentKey="background" setAllPoint="true">
+          <Color a="1" r="0" b="0" g="0" />
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture name="$parentForeground" parentKey="foreground">
+          <Color a="1" r="1" b="1" g="0" />
+        </Texture>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="TkCombatLogTemplate" parent="UIParent" virtual="true">
+    <Scripts>
+      <OnLoad>
+        print('Layout', self:GetName(), 'has arrived.')
+      </OnLoad>
+      <OnHide>
+        print('Layout', self:GetName(), ' hidden.')
+      </OnHide>
+    </Scripts>
+    <Frames>
+      <ScrollingMessageFrame>
+
+      </ScrollingMessageFrame>
+    </Frames>
+  </Frame>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Powerbar.Init.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,119 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/28/2015 8:01 AM
+
+
+Turok.defaults.powerbar = {
+  trace = true,
+  alpha_ooc = 0.1,
+  alpha_full_ooc = .1,
+  alpha = 1,
+  alpha_fade_out = 5,
+  alpha_fade_in = 0.15,
+  combatFade = true,
+  graph_safe = {.5,.5,.5,1},
+  graph_alert = {1,0,0,1},
+  graph_blend = 'ADD',
+  graph_blend_alert = 'BLEND',
+  foreground_color = {.1,.5,1  ,1},
+  background_color = {0.2,0.2,0.2,0.2},
+  y = -188,
+  x = 0,
+  height = 20,
+  width = 300,
+  padding = 0,
+  spacing = 1,
+  strata = 'BACKGROUND',
+  level = 5,
+  resource_max_noise = [[Interface\Addons\Turok\Media\sound\SquishFart.ogg]],
+  resource_low_noise = [[]],
+  resource_empty_noise = [[Interface\Addons\Turok\Media\sound\wilhelm.ogg]],
+  font = [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf]],
+  secondary = {
+    anchor = 'BOTTOMLEFT',
+    anchorTo = 'TOPLEFT',
+    parent = 'TkPowerBar',
+    spacing = 1,
+    padding = 0,
+    height = 10,
+    foreground_inset = 0,
+    foreground_color = {1,1,1,.4},
+    foreground_blend = 'ADD',
+    background_color = {0,0,0,0},
+    background_blend = 'BLEND',
+    x = 0, y = 1,
+  },
+  secondary1 = {
+    anchor = 'BOTTOMLEFT',
+    anchorTo = 'TOPLEFT',
+    parent = 'TkPowerBar',
+    spacing = 1,
+    padding = 0,
+    height = 7,
+    foreground_inset = 0,
+    foreground_color = {.7, .4, 1, 1},
+    foreground_blend = 'ADD',
+    background_color = {0,0,0,0.3},
+    background_blend = 'BLEND',
+    x = 0, y = 2,
+  },
+  secondary2 = {
+    anchor = 'BOTTOMLEFT',
+    anchorTo = 'TOPLEFT',
+    parent = 'TkPowerBar',
+    spacing = 1,
+    padding = 0,
+    height = 5,
+    foreground_inset = 0,
+    foreground_color = {1,1,1,.8},
+    foreground_blend = 'ADD',
+    background_color = {0,0,1,0},
+    background_blend = 'BLEND',
+    x = 0, y = 8,
+  },
+  secondary3 = {
+    anchor = 'BOTTOMLEFT',
+    anchorTo = 'TOPLEFT',
+    parent = 'TkPowerBar',
+    spacing = 1,
+    padding = 0,
+    height = 5,
+    foreground_inset = 0,
+    foreground_color = {1,.5,.2,1},
+    foreground_blend = 'MOD',
+    background_color = {0,0,1,0},
+    background_blend = 'BLEND',
+    x = 0, y = 16,
+
+  },
+
+  powerText = {
+    y = 0,
+    x = 6,
+    anchor='LEFT',
+    anchorTo='LEFT',
+    parent = 1,
+    justifyH = 'LEFT',
+    justifyV = 'MIDDLE',
+    text_color = {1,1,1,1},
+    size = 18,
+  },
+  secondaryText = {
+    y = 0,
+    x = -6,
+    anchor='RIGHT',
+    anchorTo='RIGHT',
+    parent = 1,
+    justifyH = 'RIGHT',
+    justifyV = 'BOTTOM',
+    text_color = {1,1,0,1},
+    size = 18,
+  },
+  graph1 = {
+    foreground_color = {.2,1,.5,1 },
+    foreground_texture = '',
+
+  },
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Powerbar.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,585 @@
+-- User: Krakyn
+-- Created: 12/15/2015 7:31 PM
+--[[
+--  Turok by @project-author@
+-- @file-author@
+-- @file-revision@:@project-revision@
+-- @file-date-iso@
+--
+-- Visible element operations begin here
+--]]
+local _G = _G
+local T, pairs, select, setmetatable, type, tinsert = _G.Turok, pairs, select, setmetatable, type, tinsert
+local mod = T:NewModule("PowerBar")
+local UnitPower, UnitPowerMax, GetTalentInfoByID, GetTalentInfo, CreateFrame = UnitPower, UnitPowerMax, GetTalentInfoByID, GetTalentInfo, CreateFrame
+local bar, db, prototype -- convenience upvalues
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cText, cNum, cWord, cKey, cPink, cBool
+  --@debug
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('PowerBar', ...)
+  end
+end
+print('Peep!', ...)
+local addon, tg = ...
+tg.what = tostring(tg.what)..'more'
+print(tg.what)
+--@end-debug@
+mod.OnInitialize = function(self)
+  self.UNIT_SPELLCAST_START = self.SpellCastEvent
+  self.UNIT_SPELLCAST_STOP = self.SpellCastEvent
+  self.UNIT_SPELLCAST_SUCCEEDED = self.SpellCastEvent
+  self.UNIT_SPELLCAST_CHANNEL_START = self.SpellCastEvent
+  self.UNIT_SPELLCAST_CHANNEL_STOP = self.SpellCastEvent
+  self.SPELL_UPDATE_COOLDOWN = self.SpellCooldownEvent
+  self.PLAYER_REGEN_DISABLED = self.CombatStart
+  self.PLAYER_REGEN_ENABLED = self.CombatEnd
+  self.focusbar = {}
+  self.parserLog = {}
+  self.currentParse = {}
+end
+local SPELL_POWER_MANA, SPELL_POWER_ENERGY, SPELL_POWER_RAGE, SPELL_POWER_FOCUS = SPELL_POWER_MANA, SPELL_POWER_ENERGY, SPELL_POWER_RAGE, SPELL_POWER_FOCUS
+local SPELL_POWER_SHADOW_ORBS = SPELL_POWER_SHADOW_ORBS
+local SPELL_POWER_SOUL_SHARDS, SPELL_POWER_BURNING_EMBERS, SPELL_POWER_DEMONIC_FURY = SPELL_POWER_SOUL_SHARDS, SPELL_POWER_BURNING_EMBERS, SPELL_POWER_DEMONIC_FURY
+local SPELL_POWER_HOLY_POWER = SPELL_POWER_HOLY_POWER
+local SPELL_POWER_CHI = SPELL_POWER_CHI
+local SPELL_POWER_COMBO_POINTS = SPELL_POWER_COMBO_POINTS
+
+-- indexes for talent_update cleanup
+mod.secondary_rows = {}
+mod.disabled_frames = {}
+
+--[[
+--  Prototype list naming all the data sources and events that need to be handled for the logged in character
+--  .power_type   {[bliz const] = event token}   list of resources represented by global SPELL_POWER_* constants in the blizzard ui and the UNIT_POWER* token argument representing it
+--       .frame   string                         frameXML template
+--      .spells   {[spell name/id] = {events}}   list of spells tracked by the updater
+--    .secondary   {[aura name] = {}}             list of auras tracked as secondary resources such as Thrill of Hunt, Anticipatin, Evangelism, etc.
+--]]
+mod.prototype = {
+  ['HUNTER'] = {
+    primary = {
+      [1] = {"FOCUS", SPELL_POWER_FOCUS}, -- array of power type constants associated to event strings
+    },
+    frame = 'TkThinComboTemplate',           ---------------------- desired frame template
+    spells = {
+      ["Steady Shot"] = {'UNIT_SPELL_CAST_SUCCEEDED', 'UNIT_SPELLCAST_STOP', 'UNIT_SPELLCAST_START'} -- spell events that this frame should listen to
+    },
+    secondary = {},
+    spec = {
+      [1] = {
+        secondary = {
+          ['Frenzy'] = {
+            type = 'aura',
+            order = 1,
+            scale = 5,
+            filters = 'HELPFUL',
+            max = 5,
+            specPage = 1,
+            unit = 'player',
+            spellID = 19623,
+          },
+          ['Focus Fire'] = {
+            type = 'aura',
+            max = 40,
+            order = 2,
+            scale = 5,
+            line = 3, --------------- use this subtext value instead of count field
+            filters = 'HELPFUL',
+            specPage = 1,
+            unit = 'player',
+            spellID = 19623,
+          }
+        }
+      },
+      [2] = {
+        secondary = {                       ------------------------ list of buffs that act as a secondary resource
+          ['Thrill of the Hunt'] = {
+            order = 1,
+            unit = 'player',
+            type = 'aura',
+            max = 3,
+            scale = 5,
+            filters = 'HELPFUL',
+            talent   = {4,3},
+            display  = 'progressbar'
+          },
+        },
+      },
+    },
+  },
+  ['PRIEST'] = {
+    primary = {
+      [1] = {'MANA', SPELL_POWER_MANA}
+    },
+    frame = 'TkThinComboTemplate',
+    secondary = {},
+    spec = {
+      [1] = {
+        secondary = {
+          ['Evangelism'] = {
+            order = 1,
+            max = 5,
+            scale = 5,
+            type='aura',
+            unit = 'player',
+            filters = 'HELPFUL|PLAYER',
+            spellID = 81662,
+          }
+        }
+      },
+      [3] = {
+        primary = {
+          [1] = {'SHADOW_ORBS', SPELL_POWER_SHADOW_ORBS},
+        },
+        secondary = {
+          ["Surge of Darkness"] = {
+            order = 2,
+            type = 'aura',
+            filters = 'HELPFUL|PLAYER',
+            spellID = 87160,
+            talentID = 21751,
+            max = 3,
+            scale = 5,
+            unit = 'player',
+          },
+          ["Insanity"] = {
+            order = 2,
+            type = 'aura',
+            binary = true,
+            regress = true,
+            size = 1, scale = 1, max = 1,
+            filters = 'HELPFUL|PLAYER',
+            spellID = 132573,
+            unit = 'player',
+            talentID = 21753},
+        }
+      },
+    },
+  },
+  ['ROGUE'] = {
+    primary = {
+      [1] = {'ENERGY', SPELL_POWER_ENERGY},
+      [2] = {'COMBO_POINTS', SPELL_POWER_COMBO_POINTS}
+    },
+    frame = 'TkThinComboTemplate',
+    secondary = {
+      ['Anticipation'] = {
+        type = 'aura',
+        order = 1,
+        max = 5,
+        scale = 5,
+        unit = 'player',
+        talentID = 19250,
+      },
+    },
+    spec = {},
+  },
+  ['MAGE'] = {
+    primary = {
+      [1] = {'MANA', SPELL_POWER_MANA},
+    },
+    frame = 'TkThinComboTemplate',
+    secondary = {
+
+      ["Incanter's Flow"] = {
+        type = 'aura',
+        unit = 'player',
+        filters = 'HELPFUL|PLAYER',
+        spellID = 1463,
+        max = 5,
+        scale = 5,
+        order = 2,
+        talentID = 16033,
+      },
+      ["Rune of Power"] = {
+        type = 'aura',
+        unit = 'player',
+        filters = 'HELPFUL|PLAYER',
+        binary = true,
+        max = 1,
+        scale = 1,
+        order = 2,
+        talentID = 16032,
+      }
+    },
+    spec = {
+      [1] = {
+        secondary = {
+          ['Arcane Charge'] = {
+            type  ='aura',
+            unit = 'player',
+            filters = 'HARMFUL|PLAYER',
+            spellID = 114664,
+            scale = 4,
+            max = 4,
+            order = 1,
+          },
+        }
+      },
+      [3] = {
+        secondary = {
+          ['Fingers of Frost'] = {
+            type = 'aura',
+            unit = 'player',
+            filters = '',
+            spellID = 112965,
+            max = 2,
+            scale = 4,
+            order = 1,
+          },
+          ['Brain Freeze'] = {
+            type = 'aura',
+            unit = 'player',
+            filters = '',
+            spellID = 44549,
+            scale = 4,
+            max = 2,
+            order = 1,
+            mirror = true,
+          }
+        }
+      },
+    }
+  }
+}
+local P = mod.prototype
+
+function mod:OnEnable()
+  self.disabled_freams = {
+    [T.playerClass] = {
+      [T.specPage] = {}
+    }
+  }
+  self.watched_units = {}
+  self.watched_auras = {}
+  self.watched_spells = {}
+  self.db = TurokData.powerbar
+  db = self.db
+
+  self:Prototype_Init()
+end
+function mod:Prototype_Init()
+  -- consult prototype vars
+  prototype = {}
+  mod.dcopy = function(t1, t2, d)
+    d = d or ''
+    for k,v in pairs(t2) do
+      if type(v) == 'table' then
+        if type(t1[k]) ~= 'table' then
+          t1[k] = {}
+          print(d, 'adding table', cKey(k))
+        else
+          print(d, 'merging tables', cKey(k))
+        end
+        mod.dcopy(t1[k], v, d..'  ')
+      else
+        if t1[k] then
+          print(d, 'clobbered', k)
+        else
+          print(d, k, '=', cType(v))
+        end
+        t1[k] = v
+      end
+    end
+  end
+  mod.dcopy(prototype, mod.prototype[T.playerClass])
+  if mod.prototype[T.playerClass].spec[T.specPage] then
+    mod.dcopy(prototype, mod.prototype[T.playerClass].spec[T.specPage])
+  end
+
+  mod.thisproto = prototype
+
+  print('|cFFFF0088Template:|r', 'Frame', 'TkPowerBarFrame', UIParent, prototype.frame)
+  db = self.db
+
+  if bar and bar.GetObjectType then
+    bar:Hide()
+    mod.disabled_frames[bar.specPage] = bar
+    print('putting away old frame')
+  end
+
+  if not bar then
+    bar = CreateFrame('Frame', 'TkPowerBar', UIParent, prototype.frame)
+  end
+  bar.specPage = T.specPage
+  bar.specID = T.specID
+  bar.primary = {}   -- {current, max, token}
+  bar.secondary = {} -- {current, max, token}
+  bar.aura = {}      -- {name, duration, expires, unit, flags}
+  bar.spell = {}     -- copy of the last T.spellevent match
+  print('  setting layout', db)
+  print(bar:GetName())
+  T.SetFrameLayout(bar, prototype.cvars and db[prototype.cvars] or db)
+    T.SetStatusTextures(bar, db)
+
+  print('  setting methods')
+  bar.Init   = mod.Bar_Init
+  bar.Event  = mod.Bar_Event
+  bar.Update = mod.Bar_Update
+
+
+  --- loop through aura definitions and flag accordingly
+  print('Primary power types:')
+  for order, power_data in pairs(prototype.primary) do
+    print( order, unpack(power_data))
+    local  token, power_type = unpack(power_data)
+    local power, max = UnitPower('player', power_type), UnitPowerMax('player', power_type)
+    bar.primary[token] = {power, max, power_type, order}
+    print('  ', cKey(token), '= {', power, max,  power_type, order, '}')
+  end
+
+  --- go through secondary data args and assign the appropriate source functions
+  local useAura, useCooldown
+  local used_rows = {}
+  if prototype.secondary then
+    mod.secondary = {}
+    for name, c in pairs(prototype.secondary) do
+      local isActive = true
+      print('parsing extra handler', name)
+      if c.talentID then
+        print(c.talentID, T.specPage)
+        isActive = (type(c.talentID) == 'table') and select(4, GetTalentInfo(unpack(c.talentID), T.specGroup)) or
+            select(4, GetTalentInfoByID(c.talentID, T.specGroup))
+        print('  talentID:', cNum(isActive))
+      end
+      if isActive then
+        local sc = {}
+
+        sc = c
+        print('  enable:', cNum(isActive), cWord(c.type))
+        if c.type == 'aura' then
+          sc.spellName = name
+          if c.binary then
+            sc.Get = function(self)
+              print('get: UnitAura', self.unit, self.spellName, c.filters)
+              local exists = UnitAura(self.unit, self.spellName, nil, self.filters)
+              return (exists) and 1 or 0
+            end
+          else
+            sc.Get = function(self)
+              print('get: UnitAura', self.unit, self.spellName, c.filters)
+              local _,_,_, count = UnitAura(self.unit, self.spellName, nil, self.filters)
+              return count or 0
+            end
+          end
+
+          useAura = true
+        elseif c.type == 'cooldown' then
+          if c.inverse then
+            sc.Get = function(self)
+              local start, duration, enabled = GetSpellCooldown(c.spellID)
+              sc[1] = (duration > 0) and (GetTime() - start) or c.max
+              print('get: GetSpellCooldown (inverse)', c.spellID, '=', sc[1])
+            end
+          else
+            sc.Get = function(self)
+              local start, duration, enabled = GetSpellCooldown(c.spellID)
+              sc[1] = (duration > 0) and (start + duration - GetTime()) or 0
+              print('get: GetSpellCooldown', c.spellID, '=', sc[1])
+            end
+          end
+          useCooldown = true
+        end
+        print('  committing', name, 'to row', sc.order)
+        bar.secondary[name] = sc
+        used_rows[sc.order] = true -- index the drawn rows for talent_update
+      end
+    end
+  end
+
+
+  if useAura then bar:RegisterEvent('UNIT_AURA') end
+  if useCooldown then bar:RegisterEvent('UNIT_SPELLCAST_SUCCEEDED') end
+
+  bar:SetScript('OnUpdate', nil) -- make sure any xml embeds are cleaned out
+  bar:SetScript('OnEvent', mod.Bar_Event)
+  bar:RegisterEvent('UNIT_POWER_FREQUENT')
+
+  bar:Init()
+  bar:Show()
+
+  -- metrics used by data plots
+  bar.width = db.width
+  bar.foreground_inset = db.foreground_inset
+  bar.right_edge = bar:GetRight()
+  bar.fill_limit = bar.right_edge
+  bar.foreground.width = bar.width + (bar.foreground_inset)
+  bar.spacing = 1
+
+  mod.powerbar = bar
+end
+
+mod.Bar_Init = function(self)
+  local mainPower, comboPower
+  for token, power in pairs(self.primary) do
+    if power[4] == 1 then
+      mainPower = power
+      elseif power[4] == 2 then
+      comboPower = power
+    end
+  end
+
+  if mainPower then
+    local power, max, type, token = unpack(mainPower)
+    if power and max then
+      self.powerText:SetText(power)
+      self:SetProgress(power/max)
+    end
+  end
+
+  if comboPower then
+    local power, max, type, token = unpack(comboPower)
+    local px = (self.width-db.secondary.spacing* (max -1)-db.secondary.padding*2) / max
+    self.combo = {}
+    for i = 1, max do
+        if not self.combo[i] then
+        self.combo[i] = self:CreateTexture('TkPrimaryResourcePellet'..i, 'OVERLAY')
+        end
+
+        local k = i - 1
+        local cx = db.secondary.padding + px * k + db.secondary.spacing * k
+        local cy = db.secondary.padding
+        self.combo[i]:ClearAllPoints()
+        self.combo[i]:SetSize(px, db.secondary.height)
+        self.combo[i]:SetPoint(db.secondary.anchor, self, db.secondary.anchorTo, cx, cy)
+        --print('    ', self.combo[i]:GetName(), self.pointsize1, cx, cy, self.combo[i]:GetDrawLayer())
+
+        self.combo[i]:Show()
+    end
+  end
+
+
+  if self.secondary then
+    if not self.resources then
+      print('|cFFFF0000creating resources block')
+      self.resources = {}
+    else
+      local hidecount = 0
+      for i, row in pairs(self.resources) do
+        for j, col in pairs(row) do
+          col:Hide()
+          hidecount = hidecount + 1
+        end
+      end
+      print('hiding', hidecount, 'regions')
+    end
+    for name, secondary in pairs(self.secondary) do
+      local n = secondary.order
+      local sid = 'secondary'..n
+      local c = db[sid] or db
+      if not self.resources[n] then
+        print('  |cFFFF8800creating resource row')
+        self.resources[n] = {}
+      end
+      local row = self.resources[n]
+
+      print('secondary resource', cText(name), 'max= '..cNum(secondary.max), 'scale= '..cNum(secondary.scale))
+      local px = c.padding
+      local pw = (self.width - c.padding*2 - c.spacing * (secondary.scale - 1)) / secondary.scale
+      for i = 1, (secondary.max or 1) do
+        if not row[i] then
+          row[i] = bar:CreateTexture('TkResourcePellet.'..tostring(secondary.order)..'.'..tostring(i))
+        end
+        row[i]:Show()
+        row[i]:SetDrawLayer('OVERLAY', sid)
+        row[i]:SetPoint('BOTTOMLEFT', self, 'TOPLEFT', px, c.y)
+        row[i]:SetSize(pw, db[sid].height or db.height)
+
+        print('  *', cNum(i), cKey(sid), cNum(px), cNum(c.padding))
+        px = px + pw + c.spacing
+      end
+    end
+  end
+
+  mod.Bar_Event(self, nil, 'player')
+end
+
+-- we only want to update at specific points
+mod.Bar_Event = function(self, event, ...)
+  local unit, token =  ...
+  _G.print('Update', event, unit, token)
+  if token and unit == 'player' then
+    mod.Bar_Power(self, token)
+  end
+  --print(unit, token, ...)
+  mod.Bar_Aura(bar, event, unit, token, ...)
+end
+
+mod.Bar_Aura = function (self, event, unit)
+  _G.print('Update','bar updating function called', event, unit)
+
+
+  if event == 'UNIT_AURA' or event == nil then
+    for token, info in pairs(self.secondary) do
+      local row = self.resources[info.order]
+      if info.unit == unit then
+        local count = info.Get(info)
+        local db = db['secondary'..info.order] or db
+        for i = 1, info.max do
+          local palette = (i > count) and ('background_color') or ('foreground_color')
+
+          print(token, i, count, (i > count), palette, unpack(db[palette]))
+          row[i]:SetTexture(unpack(db[palette]))
+        end
+      end
+    end
+  end
+end
+
+function mod:Bar_Power(token)
+  if not self.primary[token] then
+    return
+  end
+
+  local p = self.primary[token]
+  -- 1=cur, 2=max, 3=type, 4=token
+  p[1] = UnitPower('player', p[3])
+  p[2] = UnitPowerMax('player', p[3])
+  _G.print('Update',' ', table.concat(self.primary[token],', '))
+
+  if p[4] == 1 then
+    _G.print('Update', 'progress:', p[1]/p[2])
+    --print(unpack(p))
+    self.powerText:SetText(p[1])
+    self:SetProgress(p[1]/p[2])
+  elseif p[4] == 2 then
+    --print('update on', token, 'c:', p[1], 'm:', p[2])
+    self.secondaryText:SetText(p[1])
+    for i = 1, p[2] do
+      local palette = (i > p[1]) and 'background_color' or 'foreground_color'
+      self.combo[i]:SetTexture(unpack(db.secondary[palette]))
+
+    end
+  end
+end
+
+--- Spell parsing
+function mod:SpellCastEvent(e, u, spellName, rank, castID, spellID)
+  if u ~= 'player' then
+    return true
+  end
+  if e == 'UNIT_SPELLCAST_DELAYED' then
+  elseif e == 'UNIT_SPELLCAST_START' then
+    bar.casting = true
+    bar.spellevent = T.spellevent[u]
+    bar.spell = T.casting[u]
+  elseif e == 'UNIT_SPELLCAST_CHANNEL_START' then
+    bar.channeling = true
+    bar.spellevent = T.spellevent[u]
+    bar.spell = T.channeling[u]
+  elseif e == 'UNIT_SPELLCAST_SUCCEEDED' then
+  elseif e == 'UNIT_SPELLCAST_STOP' then
+    bar.casting = nil
+    bar.casting = nil
+  elseif e == 'UNIT_SPELLCAST_CHANNEL_STOP' then
+    bar.channeling = nil
+    bar.channeling = nil
+  end
+end
+
+function mod:PLAYER_TALENT_UPDATE(event, unit)
+  print(cText('*** Talent Update'), cKey('Spec:'), cWord(T.specName), cNum(T.specPage))
+  mod:Prototype_Init()
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Combat/Powerbar.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,90 @@
+<Ui>
+  <!-- placed on top of other bars; should be semi-transparent -->
+  <Texture name="TkGraph" alphaMode="ADD" virtual="true">
+    <Colors>
+      <Color r="1" g="0" b="0" a="0.5" />
+    </Colors>
+  </Texture>
+
+  <!-- StatusBar template inheritor for PowerBar frames -->
+  <Frame name="TkPowerTemplate" virtual="true" inherits="TurokStatusBar" parent="UIParent" frameStrata="BACKGROUND">
+    <Scripts>
+      <OnLoad>
+        print('PowerBar', '|cFF44FFFF'..self:GetName()..'|r loaded.')
+      </OnLoad>
+    </Scripts>
+    <Layers>
+      <Layer level="OVERLAY">
+        <FontString name="$parentPowerText" parentKey="powerText" inherits="TurokFont" justifyH="LEFT" textureSubLevel="1"/>
+        <FontString name="$parentSecondaryText" parentKey="secondaryText" inherits="TurokFontDetail" justifyH="RIGHT"  textureSubLevel="1"/>
+      </Layer>
+    </Layers>
+  </Frame>
+
+
+  <!-- Secondary powers -->
+  <Texture name="TkSecondaryPoint1" virtual="true" alphaMode="BLEND" setAllPoints="false" hidden="true" />
+  <Texture name="TkSecondaryPoint2" virtual="true" alphaMode="ADD"   setAllPoints="false" hidden="true" />
+
+
+  <Frame name="TkComboTemplate" inherits="TkPowerTemplate" virtual="true" frameStrata="BACKGROUND" hidden="true">
+    <Layers>
+      <Layer level="OVERLAY">
+        <!-- 6 max chi, 5 max combo -->
+        <Texture name="$parentCombo1_1" inherits="TkComboPoint" />
+        <Texture name="$parentCombo1_2" inherits="TkComboPoint" />
+        <Texture name="$parentCombo1_3" inherits="TkComboPoint" />
+        <Texture name="$parentCombo1_4" inherits="TkComboPoint" />
+        <Texture name="$parentCombo1_5" inherits="TkComboPoint" />
+        <Texture name="$parentCombo1_6" inherits="TkComboPoint" />
+        <!-- anticipation -->
+        <Texture name="$parentCombo2_1" inherits="TkAnticipation" />
+        <Texture name="$parentCombo2_2" inherits="TkAnticipation" />
+        <Texture name="$parentCombo2_3" inherits="TkAnticipation" />
+        <Texture name="$parentCombo2_4" inherits="TkAnticipation" />
+        <Texture name="$parentCombo2_5" inherits="TkAnticipation" />
+      </Layer>
+    </Layers>
+  </Frame>
+
+
+  <Frame name="TkThinComboTemplate" virtual="true" hidden="true" frameStrata="BACKGROUND">
+    <Layers>
+      <Layer level="BACKGROUND">
+
+        <Texture name="$parentBackground" parentKey="background" setAllPoints="true">
+          <Color r="0" g=".6" b="1" a="0.5" />
+          <Size>
+            <AbsValue y="14" />
+          </Size>
+        </Texture>
+      </Layer>
+      <Layer level="BORDER">
+
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture name="$parentForeground" parentKey="foreground" setAllPoints="true">
+          <Size>
+            <AbsValue y="14" />
+          </Size>
+          <Color r="0" g=".6" b="1" a="0.5" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <!-- 6 max chi, 5 max combo -->
+
+          <FontString name="$parentPowerText" parentKey="powerText" inherits="TurokFont" justifyH="LEFT" textureSubLevel="1">
+            <Anchors>
+              <Anchor point="BOTTOMLEFT" relativeKey="$parent.foreground" />
+            </Anchors>
+          </FontString>
+          <FontString name="$parentSecondaryText" parentKey="secondaryText" inherits="TurokFontDetail" justifyH="RIGHT"  textureSubLevel="1">
+            <Anchors>
+              <Anchor point="BOTTOMRIGHT" relativeKey="$parent.foreground" />
+            </Anchors>
+          </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Aura.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,163 @@
+--- Turok - Aura.lua
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/25/2015 5:58 AM
+-- Aura data collection
+local GetTime, UnitAura, GetSpellDescription, GetSpellInfo = GetTime, UnitAura, GetSpellDescription, GetSpellInfo
+local T, _G, tinsert = Turok, _G, tinsert
+local mod = T:GetModule("TimerControl")
+
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Aura', ...)
+  end
+end
+print('Peep!', ...)
+--@end-debug@
+
+T.defaults.spirit.aura = {
+  counterText = "%p",
+  subCounterText = "%.p",
+  chargesText = "%s",
+  justifyH = 'CENTER',
+  justifyV = 'TOP',
+  leftText = "%p",
+  rightText = "%n",
+
+  passive = {
+    icon = {
+      desaturated = false,
+      color = {1, 1, 1, 1},
+      blend = 'BLEND'
+    }
+  },
+  active = {
+    icon = {
+      desaturated = false,
+      color = {1, 1, 1, 1},
+      blend = 'BLEND'
+    }
+  },
+}
+
+local p = mod.prototype.trigger.aura
+p.class  = 'trigger'             -- identifier values that are visible from function scope
+p.type   = 'aura'
+p.cvars = { -- only define things that could break the frame here
+}
+p.events = {
+  ['UNIT_AURA'] = true,
+}
+
+--- takes user supplied values or fills itself with the cvar values provided
+p.Init = function(self, auraName, auraFilters, auraUnit)
+
+  _G.print('Prototype', 'Aura.Init')
+  self.unit = auraUnit and auraUnit or self.dvars.unit
+  self.spellName = auraName and auraName or self.spellName
+  self.filters = auraFilters and auraFilters or self.dvars.filters
+  -- set inversion states, states 0 and 1 are only processed when prevState differs
+  if self.cvars.inverse then
+    self.flags = {
+      active = 0,
+      active_prev = 2,
+      passive = 0,
+      passive_prev = 1,
+      hidden = 1,
+      hidden_prev = 0,
+    }
+    self.duration = 1
+    self.expires = 1
+  else
+    self.flags = {
+      active = 2,
+      active_prev = 0,
+      passive = 1,
+      passive_prev = 0,
+      hidden = 0,
+      hidden_prev = 1}
+  end
+  print(cWord('Load:'),cNum(self.spellID or self.inventoryID or self.itemID), cText(self.spellName))
+end
+
+p.Unload = function(self)
+  print('unloading events')
+  for k,v in pairs(p.events) do
+    self:UnregisterEvent(k)
+  end
+end
+
+--- Return current status data
+p.Query = function(self)
+  print( '    Q:', self.unit , self.spellName, nil, self.filters)
+  return UnitAura(self.unit , self.spellName, nil, self.filters)
+end
+
+p.SetText = mod.SetText
+
+--- Set supplied status data, using the list returned by p.Query()
+p.Set = function(self, ...)
+  self.active, self.rank, _, self.count, self.dispelType, self.duration, self.expires, self.caster,
+  self.isStealable, self.shouldConsolidate, self.spellID, self.canApplyAura, self.isBossDebuff = ...
+  if self.active then
+    if self.cvars.duration then
+      self.duration = self.cvars.duration
+      print(cKey('force duration ='), cNum(self.cvars.duration))
+    end
+    self.start = self.expires - self.duration
+  end
+end
+
+--- Handle in the frame itself to limit collateral from bugs
+function p.Event(self, event, unit)
+  self.event = event
+  if not event then
+    print(' DRY FIRE')
+  elseif unit ~= self.unit then
+    return
+  end
+
+  --- 3 states: nil, 0, >0
+
+  local active, rank, _, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff = self:Query()
+  local state
+  if self.cvars.duration and active ~= self.active then
+    print('passive aura with forced duration')
+    state = self.flags.active
+    duration = self.cvars.duration
+    expires = GetTime() + self.cvars.duration
+  elseif (not self.cvars.duration and (duration ~= self.duration or expires ~= self.expires)) or active ~= self.active then
+    if not active then
+      if (not self.untriggerFunc) or self:untriggerFunc() then
+        state = self.flags.hidden
+      end
+    else
+      if (not self.triggerFunc) or self:triggerFunc() then
+        if duration == 0 then
+          print('passive aura')
+          state = self.flags.passive
+        else
+          print('updating an active aura')
+          state = self.flags.active
+        end
+        self.start = expires - duration
+      end
+    end
+  end
+
+  if state then
+    T:Dispatch('TK_AURA_UPDATE', self.spellID, self.filters, state, self.displayState)
+    self:Set(active, rank, _, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff)
+    self:SetState(state)
+    print(cText('push state'), cNum(self.displayState).. ' (prev: '.. cNum(self.prevState)..')', self.timerName,  cText(active), cKey(duration), cNum(expires), cBool(self.cvars.inverse))
+  else
+    print(cText('no changes'), cNum(self.displayState).. ' (prev: '.. cNum(self.prevState)..')', cText(self.timerName))
+  end
+end
+
+p.Value = function(self)
+  return (self.charges and self.charges < self.maxCharges) and ((GetTime()  - self.chargeStart) / self.chargeDuration) or ((GetTime()  - self.start) / self.duration)
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Container.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,157 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/16/2016 12:20 AM
+local _G, CreateFrame = _G, CreateFrame
+local T, tinsert, UIParent = _G.Turok, table.insert, _G.UIParent
+local mod = T.modules.TimerControl
+local db, CollectorTray
+
+local pairs, ipairs, gsub, sub, setmetatable = pairs, ipairs, string.gsub, string.sub, setmetatable
+local INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2 =
+INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2
+--@debug@
+local DEBUG = true
+--@end-debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if not DEBUG then return end
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('TimerContainer', ...)
+  end
+end
+print('Peep!', ...)
+
+--- defaults
+local Containers = {}
+
+--- manages collections of timer displays
+local TimerContainer_Init = function(frame, cvars)
+
+  frame.num_timers = 0
+  frame.anchor = cvars.anchor
+  frame.parent = cvars.parent
+  frame.anchorTo = cvars.anchorTo
+  frame.x = cvars.x
+  frame.y = cvars.y
+  frame.name = cvars.name
+  frame.padding = cvars.padding
+  frame.spacing = cvars.spacing
+  frame.width = cvars.width
+  frame.height = cvars.height
+  frame.timers = {}
+  frame.childAnchor = cvars.childAnchor
+  frame.childAnchorTo = cvars.childAnchorTo
+
+  frame:ClearAllPoints()
+  frame:SetPoint(cvars.anchor, cvars.parent, cvars.anchorTo, cvars.x, cvars.y)
+  frame:SetSize(cvars.width, cvars.height) -- initial values
+  frame.NameText:SetText(frame.name)
+end
+
+local TimerContainer_Add = function(frame, timer)
+  if timer.containerHandle then
+    print('stop now')
+    return
+  end
+  timer.containerHandle = 1
+
+  print('adding', timer.timerName, 'to', frame.name)
+  local handle = frame.num_timers + 1
+  -- if the timer is ordered, start from the top and shift each item upward until they are no longer
+  -- above the
+  print('resulting handle:', handle)
+  print(#frame.timers)
+
+  frame.num_timers = frame.num_timers + 1
+
+  tinsert(frame.timers, timer)
+  timer.containerHandle = #frame.timers
+  --frame.timers[handle] = timer
+end
+
+local TimerContainer_Unlock = function(frame)
+
+end
+
+local TimerContainer_Update = function(frame)
+  local frameCount, hiddenCount = 0, 0
+  local w = frame.padding
+  local translation_points = {}
+  local dx, dy = 0, 0 -- net change in container dimensions
+  for k, spirit in pairs(frame.timers) do
+    hiddenCount = hiddenCount + 1
+    if spirit:IsVisible() and not(spirit.trash or spirit.cvars.absolute) then
+      frameCount = frameCount + 1
+      spirit.index = frameCount
+      print('  -', cNum(hiddenCount), cNum(frameCount), cKey(spirit:GetName()))
+      --tinsert(frame.timers, spirit)
+      spirit:ClearAllPoints()
+      local tx = w
+      local ty = 0
+      if spirit.cvars.relative then
+        tx = tx + spirit.cvars.x
+        ty = ty + spirit.cvars.y
+      else
+        w = w + spirit:GetWidth() + frame.spacing
+      end
+      translation_points[k] = {
+        x = spirit.cvars.x, y = spirit.cvars.y,
+        dx = tx - spirit.cvars.x, dy = ty - spirit.cvars.y
+      }
+    end
+
+    --- track the size of in/outbound frames
+    if not spirit.collected then
+      if spirit.trash then
+        dx = dx - spirit.width
+      elseif spirit.add then
+        dx = dx + spirit.width
+      end
+      spirit.collected = true
+    end
+  end
+  print(cText('  dx:'), cNum(dx))
+
+
+  frame.width = frame.width + dx
+  frame:SetWidth(frame.width)
+  local ddX = dx / frameCount
+
+  for id, a in pairs(translation_points) do
+    local spirit = frame.timers[id]
+
+
+    spirit.slide.t1:SetOffset(a.dx, a.dy)
+    spirit.slide:SetScript('OnFinished', function()
+      spirit.cvars.x = a.x + a.dx
+      spirit.cvars.y = a.y + a.dy
+      spirit:SetPoint(frame.childAnchor, frame, frame.childAnchorTo, spirit.cvars.x, spirit.cvars.y)
+    end)
+    spirit.slide:Play()
+  end
+end
+
+--- Updates the appropriate containers' object positions
+function mod.Report(self)
+    if not self.container then
+      self.container = 'default'
+      print('reporting to default container')
+    else
+      print('reporting to container', self.container)
+    end
+
+    if not Containers[self.container] then
+      print('need to create')
+      Containers[self.container] = CreateFrame('Frame', 'TkCollectorFrame'..self.container, UIParent, 'TkContainerTemplate')
+      TimerContainer_Init(Containers[self.container], mod.db.containers[self.container] or mod.db.containers)
+    end
+
+    if not self.containerHandle then
+      TimerContainer_Add(Containers[self.container], self)
+    else
+      print(self.timerName, 'has a container assigned')
+    end
+    TimerContainer_Update(Containers[self.container])
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Cooldown.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,345 @@
+
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/25/2015 5:33 AM
+--- Spell cooldown tracking resides here.
+--
+-- Workflow of cooldown tracking:
+--  A tracked spell cast is detected. (UNIT_SPELLCAST_*)
+--  That spell ID enters the timer table.
+--  The table is read by a handler that fires on the next frame, when cooldown information is available.  (COOLDOWN_*)
+--  Set() is called on the corresponding timer frame, and frame script takes over.
+--  Timer table spells are polled on each COOLDOWN_* event, re-applying Set() when certain conditions are met.
+--  The framescript or certain handlers will remove the timer table entry when there are no more positive conditions.
+--
+local tostring, tonumber, tinsert = tostring, tonumber, tinsert
+local GetTime, GetSpellInfo, GetInventoryItemCooldown, GetSpellCooldown, PlaySoundFile = GetTime, GetSpellInfo, GetInventoryItemCooldown, GetSpellCooldown, PlaySoundFile
+local GetSpellCharges, GetSpellCount, GetInventoryItemCount, UnitAura = GetSpellCharges, GetSpellCount, GetInventoryItemCount, UnitAura
+local IsUsableItem, IsUsableSpell, GetItemSpell = IsUsableItem, IsUsableSpell, GetItemSpell
+local xpcall = xpcall
+
+local CD_SLOT, CD_ITEM, CD_SPELL = 1, 2, 3
+local HIDDEN, PASSIVE, ACTIVE  = 0, 1, 2
+local format, ceil = string.format, math.ceil
+local strrep, gsub, pairs = string.rep, string.gsub, pairs
+local mod = Turok.modules.TimerControl
+local T = Turok
+local db
+local FADE_TIME = 0.2
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...) print('Cooldown', ...) end
+local function GetPrint (trace)
+  return trace and print or function() end
+end
+
+
+local item_spells = {
+  ['PvP Trinket'] = 42292,
+  ['Burning Mirror'] = 184270,
+}
+
+T.defaults.spirit.cooldown = {
+
+  alpha = 1,
+  alpha_ooc = 0.2,
+  inverse = false,
+  persist = false,
+  desaturated = false,
+  fill_inverse = true,
+  size = 24,
+  counterText = "%p",
+  subCounterText = "%.p",
+  chargesText = "%s",
+  justifyH = 'CENTER',
+  justifyV = 'TOP',
+
+  iconText = "%p",
+  leftText = "%p",
+  rightText = "%n / %d",
+  passive = {
+    icon = {
+      desaturated = false,
+      color = {1, 1, 1, 1},
+      blend = 'BLEND'
+    }
+  },
+  active = {
+    icon = {
+      desaturated = false,
+      color = {1, 1, 1, .6},
+      blend = 'ADD'
+    }
+  },
+
+  --- control displays of aura information in cooldown displays
+  showAura = false,
+  cooldownAura = {
+    icon = {
+      desaturated = true,
+      color = {0,1,0,1},
+    }
+  }
+}
+
+local p = mod.prototype.trigger.cooldown
+--@end-debug@
+p.class  = 'trigger'
+p.type = 'cooldown'
+p.cvars = {
+}
+--- Sets initial values before dry Event is fired to check for presence
+p.Init = function(self, spellID, caster, tristate, minValue, maxValue)
+  local print = GetPrint(self.trace)
+
+  self.spellID = spellID and spellID or self.spellID
+  self.unit = caster and caster or self.unit
+  self.persist = tristate and tristate or self.persist
+  self.minValue = minValue and minValue or tonumber(self.minValue)
+  self.maxValue = maxValue and maxValue or tonumber(self.maxValue)
+
+  --- current and last state values need to be flipped for inverted conditional
+  --- last state is defined in case it needs to be overridden to ensure proper frame update
+  print(cWord('Load:'),cNum(self.spellID or self.inventoryID or self.itemID), cText(self.spellName or GetItemSpell('player', self.inventoryID or self.itemID)) )
+  print(cWord('  inverse=')..cBool(self.cvars.inverse))
+  if self.cvars.inverse then
+    self.flags = {
+      active = HIDDEN,
+      active_prev = ACTIVE,
+      passive = PASSIVE,
+      passive_prev = PASSIVE,
+      hidden = PASSIVE,
+      hidden_prev = HIDDEN,
+    }
+  else
+    self.flags = {
+      active = ACTIVE,
+      active_prev = HIDDEN,
+      passive = PASSIVE,
+      passive_prev = HIDDEN,
+      hidden = HIDDEN,
+      hidden_prev = PASSIVE
+    }
+  end
+  if not (self.spellID or self.spellName) then
+    self.debug_info('No valid spell ID or Name')
+  end
+end
+
+local GetItemCooldown, GetItemInfo = GetItemCooldown, GetItemInfo
+p.Query = function(self)
+  local print = GetPrint(self.trace)
+  local id  = self.inventoryID or self.itemID or self.spellID
+  local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count
+
+  --- checked in order of precedence
+  if self.inventoryID then
+    print(cText('   type'), cWord('inventory slot'), cNum(id))
+    self.cooldownType = CD_SLOT
+    start, duration, enabled = GetInventoryItemCooldown('player', id)
+    charges, maxCharges, chargeStart, chargeDuration = nil, nil, nil, nil
+    count = GetInventoryItemCount('player', id)
+    usable = name and true or false
+  elseif self.itemID then
+    self.cooldownType = CD_ITEM
+
+    start, duration, enabled = GetItemCooldown(self.itemID)
+    print(GetItemCooldown(self.itemID))
+    print(GetItemInfo(id))
+
+  elseif self.spellID then
+    self.cooldownType = CD_SPELL
+    name = GetSpellInfo(self.spellID)
+    start, duration, enabled = GetSpellCooldown(self.spellID)
+    charges, maxCharges, chargeStart, chargeDuration = GetSpellCharges(self.spellID)
+    count = GetSpellCount(self.spellID)
+    usable = true -- they still exist even when dead
+  else
+    self.unit = 'notaunit'
+    T:Print('Timer \''..tostring(self.timerName)..'\' doesn\'t have a valid status ID.')
+  end
+
+  -- may not have been stored for some reason
+  if charges and not self.maxCharges then
+    self.maxCharges = maxCharges
+  end
+
+  print('cooldown.Query(',id,')', name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration)
+  return name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count
+end
+
+p.Set = function(self, ...)
+  local print = GetPrint(self.trace)
+
+  --name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count
+  local name
+  name, self.usable, self.start, self.duration, self.enabled, self.charges, self.chargeStart, self.chargeDuration, self.count = ...
+  if name then
+    self.spellName = name
+  end
+
+  if self.duration and self.start then
+    self.expires = self.start + self.duration
+  else
+    self.expires = 0
+  end
+end
+
+p.Value = function(self)
+  return (self.charges and self.charges < self.maxCharges) and ((GetTime()  - self.chargeStart) / self.chargeDuration) or ((GetTime()  - self.start) / self.duration)
+end
+
+p.SetText = mod.SetText
+
+--- Assign where meaning won't be amibiguous
+local Cooldown_OnCast = function(self)
+  self.triggerState = true
+end
+
+local Cooldown_OnUpdate = function(self, event)
+  local print = GetPrint(self.trace)
+
+  if self.triggerState then
+    print(cWord('Event'), cText(self.timerName))
+    if not event then
+      print(' *', cWord('Poke'))
+    end
+    local diff = 'start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
+        cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration)
+    local name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count = self:Query()
+
+    -- If we want and can, pull aura data and use that in place of cooldown information
+    local expires, hasAura, _
+    if self.cvars.showAura then
+      print(cText('UnitAura'), self.unit, self.spellName, nil, 'HELPFUL')
+      local name, _, _, count, _, auraDuration, auraExpires = UnitAura(self.unit , self.spellName, nil, 'HELPFUL')
+      if name and (auraDuration ~= self.auraDuration or auraExpires ~= self.auraExpires) then
+
+        print(cText('aura check ='), cBool(name), 's='..cNum(count), 'd='..cNum(auraDuration), 'e='..cNum(auraExpires))
+        start = auraExpires - auraDuration
+        duration = auraDuration
+        expires = auraExpires
+      end
+    end
+
+    --  print(name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count)
+    if duration ~= self.duration or
+          start ~= self.start or
+          chargeStart ~= self.chargeStart
+          or charges ~= self.charges then
+      print('a variable has changed')
+      local state
+
+
+      if duration == 0 and charges == self.maxCharges then
+        print(cText('  cooldown has reset, drop it from the queue'))
+        state = self.cvars.persist and self.flags.passive or self.flags.hidden
+        self.triggerState = nil
+        print('  ', cText('dropping'), cWord(self.timerName), cText('from spell tracking'))
+
+
+        self:Stats(state)
+      else
+        if duration ~= 0 then
+          print(cText('  cooldown has a hard duration'))
+          if duration > T.GCD and self.displayState ~= self.flags.active then
+            print(cText('    and its > GCD, update it'))
+            state = self.flags.active
+          end
+        end
+
+        if charges then
+          print(cText('  cooldown has charges'))
+          if charges ~= self.charges or chargeStart ~= self.chargeStart then
+            print(cText('  charges count or starting time has changed'))
+            state = self.flags.active
+          end
+        end
+
+        self:Stats(state)
+      end
+
+
+      -- form ID, id type, displayState, prevState
+      --T:Dispatch('TK_COOLDOWN_UPDATE', self.spellID, self.cooldownType, state, self.displayState)
+      if state then
+        self:Set(name, usable, start, duration, enabled, charges, chargeStart, chargeDuration, count)
+        self.expires = charges and (self.chargeStart + self.chargeDuration) or (self.start + self.duration)
+        self:SetState(state)
+        --print('  ', cText('SetState'), cNum(self.displayState), 'from', cNum(self.prevState), cWord(self.timerName))
+        print('   ',diff)
+        print('    start='..cText(self.start)..' duration='..cText(self.duration)..' charges='..
+            cText(self.charges).. ' chargeStart='..cText(self.chargeStart).. ' chargeDuration='..cText(self.chargeDuration))
+      end
+    elseif self.cooldownType == CD_SPELL then
+      if duration == 0 and charges == self.maxCharges and self.displayState == HIDDEN then
+        print(cKey(self.timerName), cText('post-framescript clean-up'))
+        self.triggerState = nil
+      end
+    end
+  end
+  --self:DumpMessages()
+
+end
+
+p.Stats = function(self, state)
+  print(self.unit, self.spellName)
+  local auraName, _, _, auraCharges, _, auraDuration, auraExpires = UnitAura(self.unit, self.spellName, nil, 'HELPFUL')
+  local name, usable, start, duration, enabled, charges, maxCharges, chargeStart, chargeDuration, count = self:Query()
+  print('# GCD =', T.GCD)
+  print('# SpellCooldown', 's =', start, 'd =', duration, 's =', charges and (charges..'/'..maxCharges) or 'NA')
+  print('# Aura', auraName and 'yes' or 'no', auraName and ('d='..cNum(auraDuration)) or '', auraName and ('e='..auraExpires))
+  print('# Frame', 'state1 =', self.displayState, 'state2 =', self.previousState, state and ('change to '..cWord(state)) or '')
+
+
+end
+
+--- Event pointers
+p.events = {
+  ['UNIT_SPELLCAST_SUCCEEDED'] = Cooldown_OnCast,
+  ['UNIT_SPELLCAST_CHANNEL_START'] = Cooldown_OnCast,
+  ['BAG_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
+  ['SPELL_UPDATE_COOLDOWN'] = Cooldown_OnUpdate,
+  ['SPELL_UPDATE_USABLE'] = Cooldown_OnUpdate,
+  ['SPELL_UPDATE_CHARGES'] = Cooldown_OnUpdate,
+}
+
+local unpack, select, debugstack = unpack, select, debugstack
+p.Event = function(self, e, ...)
+  local print = GetPrint(self.trace)
+
+  self.event = e
+  --- dry event case
+  if not e then
+    print('onEvent', self.timerName, e)
+    self.triggerState = true
+    Cooldown_OnUpdate(self, e, ...)
+  else
+    local unit = select(1,...)
+    if self.unit and unit ~= self.unit and  e ~= 'SPELL_UPDATE_COOLDOWN' then
+      return
+    end
+    local args = {...}
+    local success, d = xpcall(function() self.events[e](self, e, unpack(args)) end, function(m) print(self.name .."\n".. m .. "\n".. debugstack(3)) end)
+
+  end
+end
+
+p.triggerList = {
+  ['on_cooldown'] = function(self)
+    local start, duration, enabled = GetSpellCooldown(self.spellID)
+    local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
+    return (enabled and (duration ~= 0 or start ~= 0 or charges ~= self.maxCharges)), start, duration, enabled, charges, cStart, cDuration
+  end,
+  ['not_on_cooldown'] = function(self)
+    local start, duration, enabled = GetSpellCooldown(self.spellID)
+    local charges, _, cStart, cDuration = GetSpellCharges(self.spellID)
+    return (duration == 0 and start == 0 and charges == self.maxCharges), start, duration, enabled, charges, cStart, cDuration
+  end,
+  ['buff_active'] = function(self)
+    local name, rank, icon, count, dispelType, duration, expires, caster, isStealable, shouldConsolidate, spellID, canApplyAura, isBossDebuff, value1, value2, value3 = UnitAura(self.unit, self.spellName, self.filters)
+    return (name and true or false), expires - duration, duration, true, count, 0, 0
+  end
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Editor.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,257 @@
+--- Modules
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/18/2016 3:33 PM
+local _G = _G
+local T, type, pairs = _G.Turok, type, pairs
+local mod = T.modules.TimerControl
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if Devian and DevianDB.workspace ~= 1 then
+    print('Dialog', ...)
+  end
+end
+print('Peep!', ...)
+
+
+local TimerConfig = {
+  conf = {
+    padding = 4,
+    spacing = 1,
+    width = 450,
+  },
+  info = {
+    { key = 'timerName',
+      desc = 'Unique Name:',
+      type='EditBox', name = 'timerName', fill=true,
+      line = true,
+    },
+    { key = 'spellEnable',
+      inherits = 'TurokCheckButtonOverlay',
+      type='CheckButton', name = 'spellEnable', fixed=25, point='LEFT',
+      get = Dialog_Boolean, getarg = 'spellID',
+      collapse = true,
+      desc = 'Spell Name',
+    },
+    { key = 'spellID',
+      type='EditBox', name = 'spellName',
+      line = true, float = true
+    },
+    { key = 'fill_inverse',
+      desc = 'Reverse Fill',
+      type='CheckButton', name='fillInverse', fixed=25, point='LEFT',
+      get = Dialog_Boolean, getarg = 'fill_inverse', text='Inverted Fill',
+      line = true,
+    },
+    { key = 'display',
+      desc = 'Display Type',
+      type='EditBox', name='displayType',
+      line = true,
+    },
+  },
+}
+
+local Dialog_SetField = {}
+local dget = function(name, key)
+  if mod.db.timers[name] then
+    return mod.db.timers[name][key]
+  end
+  return nil
+end
+local inherits = {
+  EditBox = 'TkEditBox',
+  CheckButton = 'TurokCheckButtonInline',
+  Slider = 'TkSlider',
+}
+
+Dialog_SetField['CheckButton'] = function(self, checked) self:SetChecked(checked) end
+Dialog_SetField['EditBox'] = function(self, text)
+  print('     ', cKey(self:GetName()), text)
+  self:SetText(text or '') end
+Dialog_SetField['Button'] = function(self, text) self:SetText(text) end
+
+
+local function Dialog_Boolean(name, key)
+  print('  Dialog_Boolean', name, key)
+  return (mod.db.timers[name][key] and true or false)
+end
+
+function mod.Dialog_Select(self, key)
+  local timer
+  if self.parent_values[key] then
+    print('matched timer name', key)
+    timer = self.parent_values[key]
+  elseif rawget(mod.frames.spellID, key) then
+    print('matched spellID', key)
+    timer = mod.frames.spellID[key][1]
+    for k,v in pairs(timer) do
+      print(' -', k, '=', v)
+    end
+  end
+
+  if timer then
+    self.values = timer
+    self.timerName = timer.timerName
+  else
+    self.values = {
+      timerName = 'New Timer'
+    }
+    self.timerName = 'New Timer'
+  end
+  mod.Dialog_Init(self, TimerConfig.conf, TimerConfig.info)
+end
+
+function mod.Dialog_Init(self, dconf, dinfo)
+  print('init,', self.values.timerName)
+
+  if not self.fields then
+    self.fields = {}
+    self.rows = {}
+    self.Click = mod.Dialog_Click
+    self.Check = mod.Dialog_Check
+    self.EditBox = mod.Dialog_EditBox
+  end
+  print(self.name, self.timerName)
+  self.name:SetText(self.timerName)
+
+  local inset = dconf.padding + dconf.spacing
+  -- frame X max
+  local fX = 0
+  -- row number, row Y offset
+  local rn, ry = 1, -34
+  -- row x offset left-align, row x offset right-aligned, largest collapsed element
+  local rxL, rxR, rC, rh = dconf.spacing, dconf.spacing, 0, 0
+  for i, opt in ipairs(dinfo) do
+    if not self.rows[rn] then
+      self.rows[rn] = CreateFrame('Frame', self:GetName()..'Row'..rn, self, 'TurokDialogRow')
+    end
+
+    local k = opt.key
+    if self.fields[i] == nil then
+      self.fields[i] = CreateFrame(opt.type, self:GetName()..opt.name, self.rows[rn], opt.inherits or inherits[opt.type])
+      self.fields[i].index = i
+      self.fields[i].key = k
+      -- row point (from), row point (to), row x offset
+      local rp, rpt, rx
+      -- row delta
+      local rd
+      if opt.fill then
+        rpt = opt.float and 'BOTTOMLEFT' or 'BOTTOMRIGHT'
+        -- row point X offset
+        local rpx = opt.float and dconf.spacing or -dconf.spacing
+        self.fields[i]:SetPoint(rpt, self.rows[rn], rpt, rpx, 0)
+        print('    fill:', rpt, '-', rpt,' :: ', rpx, 0)
+        rd = 0
+      else
+        rd = self.fields[i]:GetWidth() + dconf.spacing
+      end
+      if opt.float then
+        rp = 'BOTTOMRIGHT'
+        rx = -rxR
+        rxR = rxR + rd
+      elseif opt.collapse then
+        rp = 'BOTTOMLEFT'
+        rx = dconf.spacing
+        rC = math.max(rC, rd + dconf.spacing) -- spacing L + rd{width + spacing R}
+      else
+        rp = 'BOTTOMLEFT'
+        rx = rxL
+        rxL = rxL + rd
+      end
+
+      rh = math.max(rh, self.fields[i]:GetHeight())
+      self.fields[i]:SetPoint(rp, self.rows[rn], rp, rx, dconf.spacing)
+      print('   align:', rp, '-', rp, ' :: ', rx, 0)
+      print('    dR:', cNum(rd), 'nR:',cWord(rn), 'rX:', cNum(rx), 'i:', cText(i))
+
+      if opt.line or (not dinfo[i+1]) then
+        print(cText'nR:', cNum(rn), 'rY:', cNum(ry))
+        self.rows[rn]:ClearAllPoints()
+        self.rows[rn]:SetPoint('TOPLEFT', self, 'TOPLEFT', dconf.padding, ry)
+        self.rows[rn]:SetPoint('TOPRIGHT', self, 'TOPRIGHT', -dconf.padding, ry)
+        self.rows[rn]:SetHeight(rh + dconf.spacing*2)
+        self.rows[rn]:Show()
+        rn = rn + 1
+        ry = ry - (rh + dconf.spacing*3) -->| {spacing T + rh + spacing B} + spacing dR |<--
+
+        print('fX:',cNum(fX), 'rX:', cNum(rxL+rxR+dconf.spacing), 'rC:', cNum(rC))
+        fX = math.max(fX, rxL+rxR+dconf.spacing)
+        fX = math.max(fX, rC)
+        rxL, rxR = dconf.spacing, dconf.spacing
+        rh = 0
+      end
+    end
+
+    self.fields[i]:Show()
+    if opt.desc and self.fields[i].description then
+      self.fields[i].description:SetText(opt.desc)
+    end
+    Dialog_SetField[opt.type](self.fields[i], opt.get and opt.get(self.values.timerName, opt.getarg) or self.values[k])
+
+  end
+  if not self.initialized then
+    self.initialized = true
+    self:SetSize(fX + dconf.padding + dconf.spacing*2, dconf.padding + math.abs(ry))
+  end
+end
+
+function mod.Dialog_Click(self,...)
+  local command = self:GetName():match("_(a%+)$")
+
+  print(command)
+end
+
+function mod.Dialog_Check(self, ...)
+    print('field #', self.index, self.key, 'checked')
+  --self:SetChecked(self:GetChecked() and false or true)
+end
+
+function mod.Dialog_EditBox(self, ...)
+  print('field #', self.index, self.key, 'changed', self:GetText())
+
+  if self.key == 'timerName' then
+    self.values.timerName = self:GetText()
+    self.name:SetText(self.values.timerName)
+    self.timerName = self.values.timerName
+  elseif self.key == 'spellID' then
+    print('handling spellID')
+  end
+
+end
+
+function mod.Dialog_Command(str, editbox)
+  --local spellID = T:GetArgs(str, 1,0)
+  local f = mod.EditDialog
+  f.values = {}
+  local db = mod.db
+
+
+  local func, t, z = pairs(db.timers) -- iterator, table
+  local name, values, y = func(t)  -- index, values
+  f.parent_values = {[name] = values}
+  f.timerName = name
+  f.values = values
+  f.values.timerName = name
+  for k,v in pairs(f.values) do
+    print(cText(k), cType(v))
+  end
+  print('pairs1', func, t)
+  print('pairs2', z, y)
+  name, values = func(t, name)
+  while name do
+    print(' entry:', name, values)
+    f.parent_values[name] = values
+    name, values = func(t, name)
+  end
+
+
+
+  if f:IsVisible() then
+    f:Hide()
+  else
+    mod.Dialog_Init(f, TimerConfig.conf, TimerConfig.info)
+
+    f:Show()
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Icon.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,245 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/15/2016 6:31 PM
+local T, _G = Turok, _G
+local mod = T.modules.TimerControl
+local GetTime, floor, unpack, tconcat = GetTime, floor, unpack, table.concat
+local HIDDEN, PASSIVE, ACTIVE = 0, 1,2
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Icon', ...)
+  end
+end
+print('Peep!', ...)
+--@end-debug@
+local GetPrint = function(trace)
+  if trace then
+    return print
+  else
+    return function() end
+  end
+end
+
+T.defaults.spirit.icon = {
+  combatFade = true,
+  alpha = 1,
+  alpha_passive = 1,
+  alpha_active = 1,
+  alpha_ooc_passive = 0,
+  alpha_ooc_active = 1,
+  alpha_ooc = 0.25,
+  size = 48,
+  container = 'default',
+  strata = 'LOW',
+  fade_in_time = 0.2,
+  fade_out_time = 0.3,
+  anchor = 'BOTTOM', anchorTo = 'BOTTOM',
+  parent = 1,
+  size = 48,
+  width = 48, height = 48,
+  x = 0,
+  y = 0,
+  strata = 'MEDIUM',
+  padding = 3,
+  spacing = 1,
+  foreground_inset=  0,
+}
+
+local p = mod.prototype.display.icon
+
+p.type='display'
+p.inherits = 'TurokIconTemplate'
+
+--- if config flags need to overrided
+p.cvars = {
+  enableIcon = true
+}
+--- Negotiate the config differences between different display states
+-- Hidden  - display is or should (via fade-out) be hidden
+-- Passive - display is visible, but no timing information is processed
+-- Active  - display is visible and counting time; on expiration, it will downgrade itself to passive or hidden
+p.Init = function(self)
+  local print = GetPrint(self.trace)
+
+  print('display.Icon.Load')
+  local c = self.cvars
+  if c.type == 'aura' then
+    self.spiral:SetReverse(true)
+  else
+    self.spiral:SetReverse(false)
+  end
+
+  if not self.icon:IsShown() then
+    self.icon:Show()
+  end
+  print('icon texture=', self.spellIcon or self.itemIcon)
+  self.icon:SetTexture(self.spellIcon or self.itemIcon)
+end
+
+
+--- Advances the display state, applying any visual transitions as necessary;
+-- @param self frame object
+-- @param newState state value; 1 for inactive, 2 for untimed active, 3 for timed active
+-- @param forcePrevious force the frame's lastState to this value to block off OnUpdate difference tests
+-- even if forced, the actual history value will still be used for method scope
+p.SetState = function(self, newState, forcePrevious)
+
+  --print(cWord(self:GetName()), 'state change issued:', cNum(state), cType(previous))
+
+  local previous = self.displayState
+  self.prevState = forcePrevious and forcePrevious or previous
+  self.displayState = newState
+  print('SetState', cNum(newState), '(from '..cType(previous)..')', cText(self.timerName))
+  --_G.print('Prototype.'..self.cvars.type, 'SetState', cNum(newState), '(from '..cType(previous)..')', cText(self.timerName))
+
+  --- Change transitions
+  if newState ~= previous then
+    print(cText('  Transition'))
+    if newState == HIDDEN then
+      print(cText('  to HIDDEN'))
+    -- to HIDDEN
+      if previous then
+      -- has to have been ACTIVE or PASSIVE at this point
+        if previous == ACTIVE then
+          print('     from ACTIVE')
+          self.spiral:StopAnimating()
+        else
+          print('     from PASSIVE')
+        end
+
+        self.Intro:Stop()
+        if self.event then
+          print('      set by event script')
+          self.Outro:Play()
+        else
+          print('      non-event source')
+          self:Hide()
+        end
+      end
+      -- want to end here if HIDDEN from nil
+    else
+      -- to ACTIVE or PASSIVE
+      self.Outro:Stop() -- stop any running outro
+
+
+      if newState == ACTIVE then
+
+        -- and is ACTIVE
+        self:Show()
+        self.spiral:Show()
+        self.spiral:SetCooldown(self.charges and self.chargeStart or self.start, self.charges and self.chargeDuration or self.duration)
+        print('spiral:Play() new', self.charges and self.chargeStart or self.start, self.charges and self.chargeDuration or self.duration)
+      end
+
+      if previous and previous ~= HIDDEN then
+        print(cText('  from vis'))
+      -- from visible
+        if self.event then
+          self.refresh = true
+          self.Retro:Play()
+        end
+      else
+        print(cText('  from non-vis'))
+        if self.event then
+          self.Intro:Play()
+        else
+          self:Show()
+        end
+      end
+    end
+  else
+    --- No-change transitions
+    if newState == ACTIVE then
+      -- ACTIVE to ACTIVE
+      print(cText(''))
+      self.spiral:Show()
+      self.spiral:SetCooldown(self.charges and self.chargeStart or self.start, self.charges and self.chargeDuration or self.duration)
+      print('spiral:Play() new', self.charges and self.chargeStart or self.start, self.charges and self.chargeDuration or self.duration)
+    else
+      print(cPink('stopping spiral'))
+      self.spiral:Hide()
+    end
+
+    -- non-HIDDEN to non-HIDDEN and not a dry fire
+    if self.event and newState ~= HIDDEN then
+      self.refresh = true
+      self.Retro:Play()
+    end
+  end
+
+  if newState ~= HIDDEN then
+    print(cText('  CVars:'))
+    local c
+    if newState == ACTIVE then
+      print('apply active profile')
+      c = self.cvars.active
+      self.fillState = 1
+    else
+      print('apply passive profile')
+      c = self.cvars.passive
+      self.fillState = 2
+    end
+
+    if self.icon and c.icon then
+
+      print(cText('    '), cWord('desat=')..cBool(c.icon.desaturated), cWord('color=')..cNum(tconcat(c.icon.color, ', ')))
+      self.icon:SetVertexColor(unpack(c.icon.color))
+      self.icon:SetDesaturated(c.icon.desaturated)
+    end
+    self:UpdateAlpha(T.inCombat, self.displayState, self.fillState)
+  end
+end
+
+p.Update = function(self)
+
+  if self.displayState == 0 and self.prevState ~= 0 then
+    print('flip to', self.displayState)
+    self.prevState = self.displayState -- quietly advance state
+    self.percent = 1
+    self.valueFull = 0
+    self.value = 0
+    self:SetText()
+  elseif self.displayState == 1 and self.prevState ~= 1 then
+    print('flip to', self.displayState)
+    self.prevState = self.displayState -- quietly advance state
+    self.valueFull = 0
+    self.value = 0
+    self.percent = 1
+    self:SetText()
+    print(self.percent, self.duration, self.start, self.expires)
+  elseif self.displayState == 2 then
+    if self.prevState ~= 2 or self.refresh then
+      print('flipped to', self.displayState)
+      self.prevState = self.displayState -- quietly advance state
+      self.refresh = nil
+    end
+    -- prevState is set externally
+    local time = GetTime()
+    if self.expires <= time and self.charges == self.maxCharges then
+      _G.print(self.cvars.type, 'timer expired, set to', (self.cvars.persist and self.flags.passive or self.flags.hidden))
+      self.percent = 1
+      self.duration = 0
+      self.expires = 0
+      self.start=  0
+      self.valueFull = self.duration
+      self.value = self.duration
+      self.elapsed = self.duration
+      self.remaining = 0
+      self:SetState(self.cvars.persist and self.flags.passive or self.flags.hidden)
+    else
+      self.percent = (self.charges and self.charges < self.maxCharges) and ((time  - self.chargeStart) / self.chargeDuration) or ((time  - self.start) / self.duration)
+      self.valueFull = self.expires - time
+      self.elapsed = time - self.start
+      self.remaining = self.duration - time
+
+      self.value = floor(self.valueFull)
+    end
+
+    --PlaySoundFile(self.cvars.sound_active)
+    self:SetText()
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Import.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,331 @@
+--- Turok - Import.lua
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/4/2016 11:07 AM
+-- Dialog interfaces and framework debugging tools; see the Google Doc
+
+local tconcat, unpack, pairs, ipairs, tostring, string = table.concat, unpack, pairs, ipairs, tostring, string
+local BOOKTYPE_SPELL, GetSpellBookItemName, GetSpellBookItemInfo = BOOKTYPE_SPELL, GetSpellBookItemName, GetSpellBookItemInfo
+local GetSpellTabInfo, GetNumSpellTabs, GetSpellInfo, GetFlyoutInfo, GetSpellDescription = GetSpellTabInfo, GetNumSpellTabs, GetSpellInfo, GetFlyoutInfo, GetSpellDescription
+local GetFlyoutSlotInfo, GetSpellBaseCooldown, FindSpellBookSlotBySpellID, IsTalentSpell = GetFlyoutSlotInfo, GetSpellBaseCooldown, FindSpellBookSlotBySpellID, IsTalentSpell
+local GetNumClasses, GetClassInfo, GetNumSpecializationsForClassID, GetSpecializationInfoForClassID
+= GetNumClasses, GetClassInfo, GetNumSpecializationsForClassID, GetSpecializationInfoForClassID
+local _G, setmetatable, type, T = _G, setmetatable, type, Turok
+local mod = Turok.modules.TimerControl
+mod.EditDialog = TkTimerConfig
+mod.SelectDialog = {}
+
+local print = function(...)
+  if Devian and DevianDB.workspace ~= 1 then
+    print('Dialog', ...)
+  end
+end
+print('Peep!', ...)
+local strpad = function(str, num)
+  return tostring(str) .. string.rep(' ', num - #tostring(str))
+end
+
+--- Handles mouse click on spellbook row frame
+mod.SpellBook_Click = function(self, btn)
+  print('mouse', btn, self.spellID)
+  mod.Dialog_Select(mod.EditDialog, self.spellID)
+end
+
+--- Creates the spell book import frame
+--
+local cmd
+function mod.Import_Open()
+  local self = mod
+  print('Spirit', 'SpecSpells')
+  local book_list = {}
+  local tab = {}
+  local list_info = {}
+  local book_max = 0
+  for k = 1, GetNumSpellTabs() do
+    local name, texture, offset, numSpells = GetSpellTabInfo(k)
+    tab[offset] = {k, name, texture, numSpells}
+    print('Spirit', name, offset, numSpells)
+    book_max = offset + numSpells
+  end
+  local tabID, tabName, tabTexture
+  local flyout, spellflyout = {}, {}
+  for i = 1, book_max do
+    if tab[i] then
+      tabID, tabName, tabTexture = unpack(tab[i])
+    end
+
+    if book_list[i] == nil then
+      local skillType, spellID = GetSpellBookItemInfo(i, BOOKTYPE_SPELL)
+      local spellNameUpper, spellSubText = GetSpellBookItemName(i, BOOKTYPE_SPELL)
+      local spellName, spellRank, spellTexture, castTime, minRange, maxRange = GetSpellInfo(spellID)
+      _G.print('Spirit', i, spellID, spellName)
+      _G.print('Spirit', '  ', skillType, spellSubText)
+
+      local spellDesc
+      if skillType == 'FLYOUT' then
+        local numSlots
+        spellName, spellDesc, numSlots = GetFlyoutInfo(spellID)
+        print('flyout #'..spellID..':', spellName)
+        flyout[spellID] = {}
+        for i=1, numSlots do
+          local fspellID, _, isKnown, fSpellName, fSpellValue = GetFlyoutSlotInfo(spellID, i)
+          print('  spell #', fspellID, fSpellName, isKnown)
+          if isKnown then
+          flyout[spellID][i] = fspellID
+          spellflyout[fspellID] = spellID
+          end
+        end
+
+      else
+        spellDesc = GetSpellDescription(spellID)
+      end
+
+      book_list[i] = {
+        skillType = skillType,
+        spellIndex = i,
+        spellID = spellID,
+        spellName = spellName,
+        spellDesc = spellDesc,
+        spellNameUpper = spellNameUpper,
+        spellSubText = spellSubText,
+        spellTexture = spellTexture,
+        spellCooldown = GetSpellBaseCooldown(spellID),
+        castTime = castTime,
+        minRange = minRange,
+        maxRange = maxRange,
+        tabID = tabID,
+        tabName = tabName,
+        tabTexture = tabTexture
+      }
+
+
+
+      end
+  end
+
+  local selector = CreateFrame('Frame', 'TkSpellSelector', UIParent, 'TurokListFrame')
+  self.frames.selector = selector
+  selector.name:SetText("Spells")
+  selector:SetPoint('TOP')
+  selector.page = 1
+
+  selector.GetRow = function(row, info, id, item_num)
+    print('  ', row:GetName(), info, id, item_num)
+    if not row.opts then
+      row.opts = {
+        CreateFrame('CheckButton', 'TkCheck_'..item_num, row, 'TurokCheckButtonInline'),
+        row:CreateFontString('TextItem_'..item_num, 'OVERLAY', 'TurokFontDetail'),
+        row:CreateTexture(nil, 'OVERLAY'),
+        row:CreateTexture(nil, 'OVERLAY'),
+        row:CreateFontString('TextItem_'..item_num..'ID', 'OVERLAY', 'TurokFontDetail'),
+      }
+    end
+    local page = book_list[id]
+    if page.skillType == 'FLYOUT' then
+      row.background:SetTexture(1,.2,.3,.4)
+
+    elseif rawget(mod.frames.spellID, page.spellID) then
+      print('has timer frame under spell #', page.spellID, mod.frames.spellID[page.spellID])
+       row.background:SetTexture(1,1,0,1)
+      local timer = mod.frames.spellID[page.spellID]
+      row:SetScript('OnMouseDown', function(self) self.clicked = true end)
+      row:SetScript('OnMouseUp', function(self) if self.clicked then self.clicked = nil mod.Dialog_Select(mod.Editor, timer.timerName) end end)
+    elseif page.spellCooldown and page.spellCooldown > 0 then
+        row.background:SetTexture(.3,.6,1,1)
+    else
+        row.background:SetTexture(0,0,0,0.3)
+    end
+
+    row:SetScript('OnMouseDown', mod.SpellBook_Click)
+
+    row.desc = page.spellDesc
+    row.spellID = page.spellID
+    row.opts[1].desc = page.skillType
+    row.opts[1]:SetSize(20,20)
+    row.opts[1].description:SetText(nil)
+    row.opts[1]:SetChecked(page.checked)
+    row.opts[2]:SetText(page.spellName)
+    row.opts[2]:SetWidth(200)
+    row.opts[2]:SetJustifyH('LEFT')
+    row.opts[3]:SetSize(20,20)
+    row.opts[3]:SetTexture(page.spellTexture)
+    row.opts[3]:SetPoint('CENTER')
+    row.opts[4]:SetSize(20,20)
+    row.opts[4]:SetTexture(page.tabTexture)
+    row.opts[4]:SetPoint('CENTER')
+    row.opts[5]:SetWidth(60)
+    row.opts[5]:SetJustifyH('LEFT')
+    row.opts[5]:SetText(page.spellID)
+  end
+
+  selector.Click = function(button, list)
+    local b = button:GetName():match("_(%a+)$")
+    if b == 'Prev' then
+      --print(b, list.offset, list.num_rows)
+      if list.page > 1 then
+        list.page = list.page - 1
+        TkList_SetView(list, list.num_rows * (list.page-1) + 1)
+      end
+      list.pagenum:SetText(list.page)
+    elseif b == 'Next' then
+      --print(' ',list.page, list.num_rows, list.max_row)
+      if (list.page) * list.num_rows < list.max_row then
+        list.page = list.page + 1
+        TkList_SetView(list, list.offset + list.num_rows)
+      end
+      list.pagenum:SetText(list.page)
+
+    elseif b == 'Add' then
+      mod.CommitBook(book_list, tab, list_info)
+    end
+    --print(list.page, list.offset, list.num_rows)
+  end
+
+  selector.Wheel = function(self, delta)
+    local offset = self.offset - delta
+    if offset > 0 and offset <= (self.max_row - self.num_rows) then
+      TkList_SetView(self, offset)
+    end
+  end
+
+  selector.Check = function(checkbutton, row, list)
+    local index = checkbutton:GetParent().actual_row
+    book_list[index].checked = (not book_list[index].checked) and true or false
+    checkbutton:SetChecked(book_list[index].checked)
+
+    if book_list[index].skillType == 'FLYOUT' then
+      for slot, spellID in pairs(flyout[book_list[index].spellID]) do
+
+        local spIndex = FindSpellBookSlotBySpellID(spellID)
+        print('  also toggling', slot, spellID, ' book slot', spIndex, book_list[spIndex].spellName)
+        book_list[spIndex].checked = book_list[index].checked
+      end
+      TkList_SetView(list, list.offset)
+    end
+  end
+  TkList_Init(selector, book_list, 1, 12)
+  selector:Show()
+  mod.SelectDialog = selector
+end
+--@end-debug@
+
+
+function mod.CommitBook(bookInfo, tabInfo, uiInfo)
+  print('committing')
+  print('  book data: ', #bookInfo, 'entries')
+  print('  tab data: ', #tabInfo, 'entries')
+
+  for index, e in ipairs(bookInfo) do
+    if e.checked then
+      local spellPretext = ''
+      if IsTalentSpell(index) then
+        spellPretext = 'Talent'
+      end
+        print('   ', strpad(spellPretext .. e.spellName, 20))
+        print('   ', strpad(e.spellSubText, 10), strpad(e.spellCooldown,5), strpad(e.skillType,6))
+
+    end
+  end
+end
+
+
+
+
+--- /tki command
+-- Constructs an index that associates global spec ID's with a list of the timers that would display under it.
+-- List entries are stored as [name] = true to prevent duplication of values in SavedVariables.
+
+function mod:CreateIndex()
+
+  --@debug@ Revert config to defaults
+  _G.TurokData = T.defaults
+  --@end-debug@
+
+  mod.db.timerindex = {}
+  setmetatable(mod.db.timerindex, {__mode = "v"}) -- ensure that dead leafs fall off
+  local index = mod.index
+  local timers = mod.timersByName
+
+  -- build class info hash and create subtables
+  local classID = {}
+  local className = {}
+  local specIDPage = {}
+  local specPageID = {}
+  local classSpecs = {}
+  print('|cFF0088FFCreateIndex|r')
+
+  --- use the internal class/specialization list
+  for i = 1, GetNumClasses() do
+    local _, tag, id = GetClassInfo(i)
+    classID[tag] = id
+    className[id] = tag
+    index[id] = {}
+    specPageID[tag] = {}
+    index[tag] = index[id]
+
+    classSpecs[tag] = GetNumSpecializationsForClassID(id)
+    for j = 1, classSpecs[tag] do
+      local specID, specName = GetSpecializationInfoForClassID(id, j)
+      print('|cFFFF0088map:|r', tag, j, specID, specName)
+      specIDPage[specID] = j
+      specPageID[tag][j] = specID
+      index[specID] = {}
+      index[id][j] = {}
+    end
+  end
+  index.global = {}
+
+  for name,timer in pairs(timers) do
+    -- class is set
+    if type(timer) == 'table' then
+      print(name)
+      if timer.playerClass then
+
+        index[timer.playerClass][name] = true
+
+        -- spec restricted
+        if timer.specPage then
+          if type(timer.specPage) ~= 'table' then
+            -- fix it up
+            timer.specPage = {timer.specPage }
+          end
+
+          print('  |cFFFFFF00class:|r', timer.playerClass .. ', |cFF99FF00spec:|r '.. tconcat(timer.specPage,', '))
+          for _, specPage in ipairs(timer.specPage) do
+            index[timer.playerClass][specPage][name] = true
+            index[specPageID[timer.playerClass][specPage]][name] = true
+          end
+
+          -- no spec restriction, copy it out
+        elseif not timer.specID then
+          print('  |cFFFFFF00class:|r', timer.playerClass .. ', |cFF99FF00spec:|r ALL')
+          for i = 1, classSpecs[timer.playerClass] do
+            local specID = specPageID[timer.playerClass][i]
+
+            index[timer.playerClass][i][name] = true             -- store for local ID
+            index[specID][name] = true -- store for global ID
+          end
+        end
+      end
+
+      if timer.specID then
+        index[timer.specID][name] = true
+      end
+
+      if not (timer.playerClass or timer.specID or timer.specPage) then
+        for tag, id in pairs(classID) do
+          index[tag][name] = true
+          for specPage, specID in pairs(specPageID[tag]) do
+            index[tag][specPage][name] = true
+            index[specID][name] = true
+          end
+        end
+        print('  |cFFFFFF00class:|r ALL, |cFF99FF00spec:|r ALL')
+      end
+    end
+  end
+
+  _G.TurokData.timerindex = index
+  T:Print('Hive data updated. /rl to commit.')
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Presets.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,394 @@
+local ADDON, Tk = ...
+local T = Tk.Addon
+local mod, tinsert, ipairs, pairs = T.modules.TimerControl, tinsert, ipairs, pairs
+local print = function(...) print('TimerPresets', ...) end
+
+
+
+--- Timer config presets
+mod.AddTimer = function(batch, container)
+  if container then
+    Turok.defaults.spirit.containers[container] = {}
+  end
+
+  for i, entry in ipairs(batch) do
+    print(cPink('AddTimer(#=')..cWord(#batch)..cPink(', ')..cWord(container)..cPink(')'))
+    if container then
+      entry.container = container
+      print('  container set to', container)
+    end
+    print('  unpacking', entry.name)
+    tinsert(Turok.defaults.spirit.timers, entry)
+  end
+end
+local tp = mod.index.preset
+
+mod.LoadPresets = function()
+  for setName, setFunc in pairs(tp) do
+    setFunc()
+  end
+end
+
+tp.Containers = function()
+  local defs = {
+    anchor = 'TOPLEFT', parent = 'TekplayerCastBar', anchorTo = 'BOTTOMLEFT', x = 0, y = -3, width = 700,
+    height = 200,
+    spacing = 3,
+    padding = 0,
+    ["default"] = {
+      name = "Default",
+      childAnchor = 'TOPLEFT',
+      childAnchorTo = 'TOPLEFT',
+    },
+    ["DoTs"] = {
+      anchor ='BOTTOM', anchorTo='TOP',
+      parent='TekplayerCastBar',
+      display = 'icon',
+      size = 48,
+      height = 48, width = 48,
+      x = 0, y = 16,
+      padding = 0,
+      spacing = 0,
+      childAnchor = 'BOTTOMLEFT',
+      childAnchorTo = 'BOTTOMLEFT',
+    }
+  }
+  for k,v in pairs(defs) do
+    TurokData.spirit.containers[k] = v
+  end
+end
+
+tp.Rogue = function()
+  mod.AddTimer(
+    {
+      {
+        name = "Vendetta",
+        type = 'cooldown',
+        display = 'icon',
+        unit = 'player',
+        spellID = 79140,
+        playerClass = 'ROGUE',
+        specPage = 1,
+      },
+      {
+        name = "Burst of Speed",
+        type = 'cooldown',
+        display = 'icon',
+        unit = 'player',
+        spellID = 108212,
+        playerClass = 'ROGUE'
+      },
+      {
+        name = "Rupture",
+        type = 'aura',
+        display = 'icon',
+        unit = 'target',
+        spellID = 1943,
+        filters = 'PLAYER|HARMFUL',
+        playerClass = 'ROGUE',
+        specPage = {1,3},
+      },
+      {
+        name = "Slice and Dice",
+        type = 'aura',
+        display = 'icon',
+        spellID = 5171,
+        unit = 'player',
+        filters = 'PLAYER|HELPFUL',
+        playerClass = 'ROGUE',
+        specPage = {2,3},
+      },
+      {
+        name = "Deadly Missing",
+        type = 'aura',
+        spellID = 2823,
+        display = 'icon',
+        unit = 'player',
+        playerClass = 'ROGUE',
+        specPage = {1,3},
+        inverse = true,
+      },
+      {
+        name = "Stealth",
+        type ='aura',
+        spellID = 1784,
+        display = 'icon',
+        unit = 'player',
+        playerClass = 'ROGUE',
+      },
+      {
+        name = "Recuperate",
+        type ='aura',
+        spellID = 73651,
+        display = 'icon',
+        unit = 'player',
+        playerClass = 'ROGUE',
+      },
+    })
+end
+tp.Mage = function()
+  mod.AddTimer({
+    {
+      name = "Rune of Power",
+      type = 'aura',
+      spellID = 116014,
+      unit = 'player',
+      display = 'icon',
+      playerClass = 'MAGE',
+      talentID = 16032,
+    },
+    {
+      name = "Mage Nova",
+      spellID = {157980, 157981, 157997},
+      talentOffset = {5, 3},
+      unit = 'player',
+      type='cooldown',
+      display='icon',
+      playerClass='MAGE',
+      persist = true,
+    },
+    {
+      name = "Mage Bomb",
+      talentOffset = {5, 1},
+      unit = 'target',
+      type= 'aura',
+      display='icon',
+      filters = 'HARMFUL|PLAYER',
+      playerClass='MAGE',
+      persist = true,
+    },
+    {
+      name = "Frozen Orb",
+      spellID = 84714,
+      threschold = 2,
+      unit = 'player',
+      type='cooldown',
+      display='icon',
+      playerClass='MAGE',
+      specPage=3,
+    },
+    {
+      name = 'Arcane Power',
+      type = 'cooldown',
+      display = 'icon',
+      unit = 'player',
+      spellID = 12042,
+      playerClass = 'MAGE',
+      specPage = 1,
+    }
+  })
+end
+tp.UseEffects = function()
+  mod.AddTimer({
+
+    {
+      name = "Trinket 1",
+      type ='cooldown', display = 'icon',
+      unit = 'player',
+      persist = true,
+      inventoryID = 13,
+    },
+    {
+      name = "Maalus Effect", -- Maalus
+      playerClass = {'HUNTER', 'ROGUE'},
+      type = 'aura', display = 'progressbar',
+      unit = 'player', filters = 'HELPFUL',
+      leftText = "%c", rightText=  "%p",
+      duration = 15,
+      spellID = 187615, itemID  = 124636, hideIcon = true,
+
+      sound_active = [[Interface\Addons\Turok\Media\sound\FLASH.mp3]],
+
+      absolute = true, x = 0, y = -10, width = 300, height = 10,
+      anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOMLEFT',
+      parent = 'TekplayerCastBar', padding = 0, spacing = 0,
+
+      foreground_inset = 0, foreground_color = {1,1,1,1}, foreground_blend = 'ADD',
+      background_color = {0,0,0,0},
+      icon = false,
+    },
+
+    {
+      name = "Maalus CD",
+      type = 'cooldown', display = 'icon',
+      unit = 'player',
+      spellID = 187615, itemID  = 124636,
+      playerClass = 'HUNTER',
+    },
+  })
+end
+
+tp.Hunter = function()
+  mod.AddTimer({
+    ---- Icon templates
+    { virtual = true, name = "Hunter",
+      playerClass = 'HUNTER', unit = 'player', container = 'default', display= 'icon', },
+    { virtual = true, name = "HunterSticky", inherits = "Hunter",
+      showAura = true, persist = true, },
+    { virtual = true, name = "HunterBar",
+      playerClass = 'HUNTER', unit = 'player', },
+    ---- Talents
+    {
+      inherits= "HunterSticky",
+      name = "Heavy Artillery", type = 'cooldown',
+      talentRow = 6,
+      trace = true},
+    {
+      inherits= "HunterSticky",
+      name = "Animal Power", type = 'cooldown',
+      talentRow = 5,
+      trace = true},
+    --- Spells
+    {
+      inherits = 'Hunter', name = "Camouflage",
+      type = 'cooldown', display = 'icon',
+      spellID = 51753, },
+    {
+      inherits = 'Hunter', name = "Ice Trap",
+      type = 'cooldown', display = 'icon',
+      spellID = 13809, },
+    {
+      inherits = 'Hunter', name = "Concussive Shot",
+      type = 'cooldown', display = 'icon',
+      spellID = 5116, },
+    {
+      inherits = 'Hunter', name = "Flare",
+      type = 'cooldown', display = 'icon',
+      spellID = 1543, },
+    {
+      inherits = 'Hunter', name = "Disengage",
+      type = 'cooldown', display = 'icon',
+      spellID = 781, },
+    {
+      inherits = "HunterSticky", name = "Rapid Fire",
+      type = 'cooldown', display = 'icon',
+      spellID = 3045, specPage = 2, },
+    {
+      inherits = 'HunterSticky', name = "Bestial Wrath",
+      type = 'cooldown', display = 'icon',
+      spellID = 19574, specPage = 1,
+      trace = true
+    },
+
+    --- Progress Bars
+    {
+      inherits = 'HunterBar', name = "Chimaera Shot",
+      type = 'cooldown', display = 'progressbar',
+      spellID = 53209, specPage = 2, persist = true,
+
+      hideIcon = true,
+      leftText = "", rightText = "%p",
+      width = 300, height = 8,padding = 0, spacing = 0,
+      absolute = true, x = 0, y = 0, anchor = 'TOPLEFT', anchorTo = 'TOPLEFT', parent = 'TekplayerCastBar', strata='MEDIUM',
+      foreground_color = {1,.125,0.43,1}, foreground_blend = 'ADD', foreground_inset = 0,
+      background_color = {.5,.5,.5,0}, background_blend = 'BLEND',
+    },
+    {
+      inherits = 'HunterBar', name = "Sniper Training (duration)",
+      type = 'aura', display = 'progressbar',
+      spellID = 168811, specPage = 2,
+
+      hideIcon = true,
+      leftText = "", rightText=  "",
+      width= 330, height = 12, padding = 0,
+      absolute = true, x = 0, y = 0, anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOMLEFT', parent = 'TekplayerCastBar', level = 1,
+      foreground_color = {1,0,0,1}, foreground_blend = 'BLEND',
+      sound_active = '', sound_hidden = '', sound_passive = '',
+    },
+    {
+      inherits = 'HunterBar', name = "Sniper Training: Recently Moved",
+      type = 'aura', display = 'progressbar',
+      spellID = 168809, specPage = 2,
+
+      hideIcon = true,
+      leftText = "", rightText=  "",
+      height = 12, width= 160, padding = 0,
+      absolute = true, x = 0, y = 0, anchor = 'BOTTOMLEFT', anchorTo = 'BOTTOMLEFT', parent = 'TekplayerCastBar', level = 2,
+      foreground_color = {1,1,0,1}, foreground_blend = 'BLEND',
+      sound_active = '', sound_hidden = '', sound_passive = '',
+    },
+
+    --- Static Warnings
+    {
+      inherits='Hunter', name = "Aspect of the Pack",
+      type = 'aura', display = 'icon',
+      spellID = 13159
+    },
+    {
+      inherits = 'Hunter', name = "Trap Launcher",
+      type = 'aura', display = 'icon',
+      spellID = 77769, inverse = true,
+
+      absolute = true, x = 0, y = 200,
+      width = 100, height = 100,
+      foreground_color = {1,1,1, 0.5}, foreground_blend = 'BLEND',
+    },
+  })
+end
+tp.SPriest = function()
+  mod.AddTimer( {
+    {
+      virtual = true, name = "Caster Icon", container = 'DoTs',
+      playerClass = 'PRIEST',
+
+      combatFade = true,
+      height = 48, width = 48,
+      icon = { size = 48, },
+      persist = true,
+    },
+    {
+      inherits = "Caster Icon", name = "Shadowfiend",
+      unit = "player", spellID = 132603,
+      type = 'cooldown', display = 'icon',
+      container ='DoTs',
+    },
+    {
+      inherits = "Caster Icon", name = "Insanity",
+      type = 'aura', display = 'icon',
+      unit = 'player', filters = 'PLAYER|HELPFUL',
+      spellID = 132573, playerClass = 'PRIEST',
+      order = 3,
+    },
+    {
+      name = "Shadow Word: Pain",
+      type = 'aura', display = 'icon',
+      unit = 'target', filters = 'PLAYER|HARMFUL',
+      spellID = 589, playerClass = 'PRIEST', specPage = 3,
+
+      container = 'DoTs',
+      order = 1,
+      sound_active = '',
+    },
+    {
+      name = "Mental Fatigue",
+      type = 'aura', display = 'icon',
+      unit = 'target', filters = 'PLAYER|HARMFUL',
+      spellID = 184915, playerClass = 'PRIEST', specPage = 3,
+
+      parent = 'TekplayerCastBar',
+      container = 'DoTs',
+      order = 1,
+      sound_active = '',
+    },
+    {
+      name = "Vampiric Touch",
+      container = 'DoTs',
+      type = 'aura', display = 'icon',
+      unit = 'target', filters = 'PLAYER|HARMFUL',
+      spellID = 34914, playerClass = 'PRIEST', specPage = 3,
+      order =2,
+    },
+    {
+      name = "PW:Shield",
+      type = 'cooldown', display = 'icon',
+      unit = 'player',
+      spellID = 17, playerClass = 'PRIEST',
+    },
+    {
+      name = "Cascade",
+      type = 'cooldown', display = 'icon',
+      unit = 'player',
+      spellID = 127632, talentID = 21718, playerClass = 'PRIEST',
+    },
+  })
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Progressbar.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,246 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/15/2016 6:38 PM
+local T, _G = Turok, _G
+local GetTime, PlaySoundFile, format = GetTime, PlaySoundFile, string.format
+local unpack, tconcat = unpack, table.concat
+local ACTIVE, PASSIVE, HIDDEN = 2, 1, 0
+local mod = T.modules.TimerControl
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Progressbar', ...)
+  end
+end
+print('Peep!', ...)
+--@end-debug@
+local GetPrint = function(trace)
+  if trace then
+    return print
+  else
+    return function() end
+  end
+end
+T.defaults.spirit.progressbar = {
+  combatFade = true,
+  alpha = 1,
+  alpha_ooc = 0,
+  alpha_ooc_passive = 0,
+  alpha_ooc_active = 1,
+
+  width = 200, height = 24,
+  parent = 1,
+  anchor = 'CENTER', anchorTo = 'CENTER',
+  setAllPoints = true,
+  strata = 'MEDIUM',
+
+  foreground_color = {1,0.5,0,0.5},
+  foreground_blend = 'ADD',
+  foreground_texture = [[Interface\Addons\Turok\Media\statusbar\Minimalist.tga]],
+  background_color = {1,1,1,0.5},
+  background_blend = 'BLEND',
+  padding = 3,
+  spacing = 1,
+  foreground_inset=  0,
+  icon = {
+    embedded = true,
+    alpha = 1,
+    alpha_ooc = 1,
+    size = 24,
+    x = -6, y = 0,
+    anchor = 'RIGHT', anchorTo = 'LEFT',
+    parent = 1,
+  },
+
+  -- text
+  color = {0,0,0,.5},
+}
+
+local p = mod.prototype.display.progressbar
+p.type='display'
+
+p.inherits = 'TurokProgressbarTemplate'
+p.cvars = {}
+
+--- Load-time config retrieval
+p.Init = function (self)
+  local print = GetPrint(self.trace)
+
+  print(' ', self:GetName(),'<- Progressbar.Load')
+  print(' ', self:GetHeight())
+
+  if self.cvars.hideIcon then
+    self.enableIcon = false
+    print('Icon hidden')
+  else
+    self.icon:Hide()
+      if self.cvars.icon then
+      print('Icon data:')
+      for k,v in pairs(self.cvars.icon) do
+        print('   ', k, '=', v)
+      end
+      print(cWord('  icon=')..cText(self.itemIcon or self.spellIcon))
+      self.icon:SetTexture(self.itemIcon or self.spellIcon)
+      self.icon:ClearAllPoints()
+      T.SetTextureLayout(self.icon, self.cvars.icon)
+      self.enableIcon = true
+      self.icon:Show()
+    end
+
+  end
+
+  T.SetStatusTextures(self, self.cvars)
+
+  _G.print('Update', self.background:GetWidth(), self.background:GetHeight())
+  _G.print('Update', self.foreground:GetWidth(), self.foreground:GetHeight())
+end
+
+
+--- Negotiate the config differences between different display states
+-- Hidden  - display is or should (via fade-out) be hidden
+-- Passive - display is visible, but no timing information is processed
+-- Active  - display is visible and counting time; on expiration, it will downgrade itself to passive or hidden
+p.SetState = function(self, newState, forcePrevious)
+  local print = GetPrint(self.trace)
+
+  local previous = self.displayState
+  self.prevState = forcePrevious and forcePrevious or previous
+  self.displayState = newState
+
+  local newState, previous = self.displayState, self.prevState
+  --- Transition
+  if newState ~= previous then
+    print(cText('  Transition:'), cWord(self.spellName))
+    if newState == HIDDEN then
+      print(cText('  to HIDDEN'))
+      -- to HIDDEN
+      if previous then
+        -- has to have been ACTIVE or PASSIVE at this point
+        self.Intro:Stop()
+        if self.event then
+          self.Outro:Play()
+        else
+          self:Hide()
+        end
+      end
+      -- want to end here if HIDDEN from nil
+    else
+      -- to ACTIVE or PASSIVE
+      self.Outro:Stop() -- stop any running outro
+
+
+      if newState == ACTIVE then
+
+        -- and is ACTIVE
+        self:Show()
+      end
+
+      print(cText('  from'), cNum(previous))
+      if previous and previous ~= HIDDEN then
+        -- from visible
+        if self.event then
+          self.refresh = true
+          self.Retro:Play()
+        end
+      else
+        if self.event then
+          self.Intro:Play()
+        else
+          self:Show()
+        end
+      end
+    end
+  else
+    if newState == ACTIVE then
+      print(cText(''))
+      -- and is ACTIVE
+    end
+
+    if self.event and newState ~= HIDDEN then
+      self.refresh = true
+      self.Retro:Play()
+    end
+  end
+
+  if newState ~= HIDDEN then
+    print(cText('  CVars:'))
+    local c = (newState == ACTIVE) and self.cvars.active or self.cvars.passive
+    if self.icon and c.icon then
+      print(cText('    '), cWord('desat=')..cBool(c.icon.desaturated), cWord('color=')..cNum(tconcat(c.icon.color, ', ')))
+      self.icon:SetVertexColor(unpack(c.icon.color))
+      self.icon:SetDesaturated(c.icon.desaturated)
+    end
+    local c
+    if newState == ACTIVE then
+      self.fillState = 1
+      c = self.cvars.active
+    else
+      self.fillState = 2
+      c = self.cvars.passive
+    end
+
+    if self.icon and c.icon then
+      print(cText('    '), cWord('desat=')..cBool(c.icon.desaturated), cWord('color=')..cNum(tconcat(c.icon.color, ', ')))
+      self.icon:SetVertexColor(unpack(c.icon.color))
+      self.icon:SetDesaturated(c.icon.desaturated)
+    end
+
+    self:UpdateAlpha(T.inCombat)
+  end
+end
+
+p.Update = function (self)
+  local print = GetPrint(self.trace)
+
+  -- Trigger business
+  -- Passive or Hidden, evaluate once
+  if self.displayState == 0 and self.prevState ~= 0 then
+    self.prevState = self.displayState -- quietly advance state
+    self.percent = 0
+    self.valueFull = 0
+    self.value = 0
+    self:SetText()
+    self:SetProgress(self.percent)
+    print('go LOW')
+  elseif self.displayState == 1 and self.prevState ~= 1 then
+    self.prevState = self.displayState -- quietly advance state
+    self.valueFull = 0
+    self.value = 0
+    self.percent = 1
+    self:SetText()
+    self:SetProgress(self.fill_inverse and 0 or 1)
+    print('go PASSIVE', self.percent, self.duration, self.start, self.expires)
+  elseif self.displayState == 2 then
+    if self.prevState ~= 2 or self.refresh then
+      self.prevState = self.displayState -- quietly advance state
+      self.refresh = nil
+    end
+    -- prevState is set externally
+    local time = GetTime()
+    if self.expires <= time and self.charges == self.maxCharges then
+      self.percent = 1
+      self.duration = 0
+      self.expires = 0
+      self.start=  0
+      self.valueFull = self.duration
+      self.value = self.duration
+      self.elapsed = self.duration
+      self.remaining = 0
+      self:SetState(self.cvars.persist and self.flags.passive or self.flags.hidden)
+    else
+      self.valueFull = self.expires - time
+      self.percent = self.valueFull / self.duration
+      self.elapsed = time - self.start
+      self.remaining = self.duration - time
+
+      self.value = floor(self.valueFull)
+    end
+
+    --PlaySoundFile(self.cvars.sound_active)
+    self:SetText()
+    self:SetProgress(self.percent)
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Status.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,278 @@
+local _G, T = _G, Turok
+local mod = T.modules.TimerControl
+local P = mod.prototype
+local GetInventoryItemID, GetItemInfo, GetInventoryItemTexture = GetInventoryItemID, GetItemInfo, GetInventoryItemTexture
+local GetSpellDescription, GetSpellInfo, GetSpellCharges = GetSpellDescription, GetSpellInfo, GetSpellCharges
+local GetTalentRowSelectionInfo = GetTalentRowSelectionInfo
+local tinsert, type, tonumber, pairs, ipairs, unpack = tinsert, type, tonumber, pairs, ipairs, unpack
+local UnitGUID = UnitGUID
+local GetTalentInfo, GetTalentInfoByID, GetItemSpell, PaperDoll_IsEquippedSlot = GetTalentInfo, GetTalentInfoByID, GetItemSpell, PaperDoll_IsEquippedSlot
+local print = function(...) print('Timer', ...) end
+print('Peep!', ...)
+
+--@debug@
+local DEBUG = true
+--@end-debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if not DEBUG then return end
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Timer', ...)
+  end
+end
+------------------------------------------
+--- Parameter Prototypes
+
+local Timer_GetPrintHandler = function(self)
+  return self.trace and function(...)
+    print(...)
+    _G.print('TimerFocus', ...)
+  end or print
+end
+
+local Timer_UpdateIndex = function(self, key, value)
+  local print = Timer_GetPrintHandler(self)
+  --       sfk[value] = frame index key-value handle
+  --             mfkv = key-value frame index
+  -- mfkv[sfk[value]] = *frame
+  if self.frames[key] then
+    local sfk = self.frames[key]
+    -- for each unique value
+    for oldValue, index in pairs(sfk) do
+      local mfkv = mod.frames[key][oldValue]
+      -- for each offset after index
+      for i = index+1, #mfkv do
+        mfkv[i-1] = mfkv[i] -- slide it down
+      end
+      sfk[oldValue] = nil
+      print(cText('    *= self.frames')..'['..cKey(key)..']['..cNum(value)..'] =', cNum(self.frames[key][value]))
+      print(cText('    *= mod.frames')..'['..cKey(key)..']['..cNum(value)..']['..cNum(index)..'] =', cNum(self:GetName()))
+    end
+  else
+    self.frames[key] = {}
+  end
+
+  if value then
+    local mfkv = mod.frames[key][value]
+    local sfk = self.frames[key]
+    mfkv[#mfkv+1] = self
+    sfk[value] = #mfkv
+    print('    *= self.frames['..cKey(key)..']['..cNum(value)..'] = ', sfk[value])
+  end
+end
+
+--- spellID
+do
+  local spellID = P.status.spellID
+  spellID.Init = function(self, spellID)
+    local print = Timer_GetPrintHandler(self)
+
+    if type(spellID) == 'table' then
+      spellID = spellID[T.specPage]
+    end
+
+    -- May have changed if invoked through a slot assignment
+    if spellID ~= self.spellID then
+      self.spellID = spellID
+      self.charges, self.maxCharges, self.chargeStart, self.chargeDuration = GetSpellCharges(spellID)
+      self.spellDescription = GetSpellDescription(spellID)
+      self.spellName, self.spellRank, self.spellIcon, self.castingTime, self.minRange, self.maxRange = GetSpellInfo(spellID)
+      --@debug@
+      print('   ', cWord('spellID ='), cKey(self.spellID), cNum(self.spellName), cText(self.spellDescription and '<desc>' or '<empty>'))--@end-debug@
+
+      Timer_UpdateIndex(self, 'spellID', spellID)
+    end
+  end
+end
+
+--- spellName
+do
+  local spellName = P.status.spellName
+  spellName.Init = function(self, spellName)
+    local print = Timer_GetPrintHandler(self)
+    -- attempt to get spell info
+    local exists, _, _, _, _, _, spellID =  GetSpellInfo(spellName)
+    if exists then
+      local charges, maxCharges, start, duration = GetSpellCharges(spellID or spellName)
+      Timer_UpdateIndex(self, 'spellName', nil)
+      print('   ', cKey("spellID"), '=', spellID)
+      P.status.spellID.Init(self, spellID)
+    else
+      print('   ', cPink("spellID"), '=', 'not sure')
+      Timer_UpdateIndex(self, 'spellID', nil)
+      Timer_UpdateIndex(self, 'spellName', spellName)
+    end
+  end
+end
+
+
+--- talentID
+do
+  local talentID = P.status.talentID
+  local GetTalentInfoByID = GetTalentInfoByID
+  talentID.type = 'status'
+  talentID.Init = function(self, talentID)
+    local print = Timer_GetPrintHandler(self)
+
+    self.talentID, self.spellName, self.spellIcon, self.talentSelected = GetTalentInfoByID(talentID, T.specGroup)
+
+    if self.talentSelected then
+      print('   ', cKey("spellName"), '=', cWord(self.spellName))
+      P.status.spellName.Init(self, self.spellName)
+    else
+      self.disable =  true
+      self.debug_info ('Talent not selected.')
+    end
+
+    print('   ', cKey('talentID ('..cNum(self.talentID)..', '..cNum(T.specGroup)..'):'), self.spellName, self.talentSelected )
+    Timer_UpdateIndex(self, 'talentID', talentID)
+
+  end
+end
+
+--- talentRow - use whatever is in that row as data
+do
+  local talentRow = P.status.talentRow
+  talentRow.Init = function(self, row)
+    local print = Timer_GetPrintHandler(self)
+    self.talentRow = row
+    local noSelect, talentID = GetTalentRowSelectionInfo(row)
+    if noSelect then
+      self.disable = true
+      self.debug_info("No talent selected in target row.")
+    else
+      P.status.talentID.Init(self, talentID)
+    end
+    print('   ', cKey('talentRow ('.. cNum(row).. ') ='), talentID or 'none')
+    Timer_UpdateIndex(self, 'talentRow', row)
+  end
+end
+
+
+--- talentOffset
+do
+  local talentOffset = P.status.talentOffset
+  talentOffset.Init = function(self, coords)
+    local print = Timer_GetPrintHandler(self)
+
+    local tier, column = unpack(coords)
+
+
+    local selected, usable, _
+    self.talentID, self.spellName, self.spellIcon, selected, usable = GetTalentInfo(tier, column, T.specGroup)
+    print('   ', cKey('talentOffset'), '=', cNum(tier), cNum(column), self.spellName, selected, usable)
+    if not (selected and usable) then
+      self.disable =  true
+      self.debug_info ((not usable) and 'Unavailable at current level.' or 'Talent not selected.')
+    end
+    Timer_UpdateIndex(self, 'talentID', self.talentID)
+  end
+end
+
+--- specPage
+do
+  local specPage = P.status.specPage
+  specPage.type = 'status'
+  specPage.Init = function(self, specPage)
+    local print = Timer_GetPrintHandler(self)
+    local match
+    if tonumber(specPage) then
+      match = specPage == T.specPage
+    elseif type(specPage) == 'table' then
+      for _, pageID in ipairs(specPage) do
+        if T.specPage == pageID then
+          specPage = pageID
+          match = true
+          break
+        end
+      end
+    else
+      self.disable =  true
+      self.debug_info ('Bad value for', '('..cWord(self.timerName)..').'..cKey('specPage'))
+      Timer_UpdateIndex(self,'specPage', nil)
+    end
+
+    if match then
+      print('   ', cKey('specPage'), '=', '['..cNum(specPage)..']')
+      Timer_UpdateIndex(self,'specPage', specPage)
+    else
+      print('   ', cKey('specPage'), '~=', '['..cNum(specPage)..']')
+      self.disable = true
+      self.debug_info('Not active spec.')
+    end
+  end
+end
+
+--- itemID
+do
+  local itemID = P.status.itemID
+  itemID.type  = 'status'
+  itemID.Init = function(self, item)
+    local print = Timer_GetPrintHandler(self)
+
+    if not self.dvars.inventoryID then
+      self.itemID = item or self.dvars.itemID
+      self.itemType = self.dvars.itemType
+    else
+      self.itemID = item
+    end
+
+    --@debug@
+    --name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID) or GetItemInfo("itemName") or GetItemInfo("itemLink")
+    if not self.itemID then
+      self.disable =  true
+       self.debug_info ('bad itemID value')
+    end
+
+    self.spellName = GetItemSpell(self.itemID)
+
+    if not self.spellName then
+      self.disable =  true
+       self.debug_info('no spell detected')
+    else
+      if not (self.frames.spellName and self.frames.spellName[self.spellName]) then
+        if not self.frames.spellName then
+          self.frames.spellName = {}
+        end
+
+        Timer_UpdateIndex(self, 'spellName', self.spellName)
+      end
+    end
+    print('   ', cKey('itemID'), cNum(self.itemID), self.isEquipped)--@end-debug@
+  end
+end
+
+do
+  local inventoryID = P.status.inventoryID
+  inventoryID.type = 'status'
+  inventoryID.Init = function(self, slot)
+    local print = Timer_GetPrintHandler(self)
+
+    self.inventoryID = slot
+    self.itemID = GetInventoryItemID(self.dvars.unit, slot)
+    local name = GetItemInfo(self.itemID)
+    if not PaperDoll_IsEquippedSlot(slot) then
+      self.disable = true
+      self.debug_info('slot un-equipped')
+      return
+    end
+
+    P.status.itemID.Init(self, self.itemID)
+    self.spellIcon = GetInventoryItemTexture(self.dvars.unit, slot)
+    print('   ', cKey('inventoryID'), '=', '{'..cNum(self.inventoryID).. ' -> '.. cNum(self.itemID or 'no-equip').. '}')
+  end
+end
+
+
+--- unit
+local unit = P.status.unit
+unit.type = 'status'
+unit.Init = function(self, unit)
+  local print = Timer_GetPrintHandler(self)
+
+  self.unitGUID = UnitGUID(unit)
+  self.unit = unit
+  print('   ', cWord('unitGUID'), '=', cText(self.unitGUID))
+  tinsert(mod.frames.unit[unit], self)
+  self.frames.unit = #mod.frames.unit[unit]
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Timer.Init.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,205 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 12/28/2015 7:40 AM
+--
+local _, Tk = ...
+local T = Tk.Addon
+local mod = T:NewModule("TimerControl", "AceTimer-3.0")
+local pairs, ipairs, rawset, getmetatable, setmetatable, type, tostring, tinsert = pairs, ipairs, rawset, getmetatable, setmetatable, type, tostring, tinsert
+local cWord, cNum, cText, cKey = cWord, cNum, cText, cKey
+local print = function(...) print('Timer', ...) end
+local index_mt = {
+    __newindex = function(t,k,v)
+      rawset(t,k,v)
+      if type(v) == 'table' then
+        print('new table', cKey(k))
+        setmetatable(v, getmetatable(t))
+      else
+        print('new index', cText(k))
+      end
+    end
+  }
+mod.index = {}
+mod.index.name = setmetatable({}, index_mt)
+mod.index.global = setmetatable({}, index_mt)
+mod.index.preset = {}
+-- dummies
+mod.prototype = {
+  status = {
+    spellID = {},
+    spellName = {},
+    talentID = {},
+    talentRow = {},
+    talentOffset = {},
+    itemID = {},
+    inventoryID = {},
+    specPage = {},
+    specID = {},
+    unit = {},
+  },
+  trigger = {
+    aura = {},
+    cooldown = {},
+    complex = {},
+  },
+  display = {
+    icon = {},
+    progressbar = {},
+  },
+}
+
+Turok.defaults.spirit = {
+  global = {
+    alpha = 1,
+    alpha_ooc = 0.5,
+
+    sound_active = '[[Interface\Addons\Turok\Media\sound\link.ogg]]',
+    sound_passive = [[Interface\Addons\Turok\Media\sound\Heart.ogg]],
+    sound_hidden = [[Interface\Addons\Turok\Media\sound\Electro_-S_Bainbr-7955_hifi.mp3]],
+
+    strata = 'LOW',
+    anchor = 'CENTER',
+    parent = 'UIParent',
+    anchorTo = 'CENTER',
+    width = 100,
+    height = 100,
+    x = 0,
+    y = 100,
+    alpha = 1,
+    alpha_ooc = 0.3,
+    inverse = false,
+    persist = false,
+    desaturated = false,
+
+    icon = {},
+
+    passive = {
+      icon = {}
+    },
+    active = {
+      inco = {}
+    },
+  },
+
+  font = [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Regular.ttf]],
+  lefttext = {
+    size = 14,
+    inset = -2,
+    point = 'LEFT',
+    outline = 'OUTLINE',
+    format = '%n %pd',
+    text_color = {1, 1, 1, 1},
+  },
+  containers = {
+  },
+  timers = {},
+}
+
+local tdb = Turok.defaults.spirit.timers
+
+mod.GetInherited = function(dvars, merge, inherited)
+  if not inherited then
+    inherited = {}
+  end
+  inherited[merge] = merge
+  T.Config_Merge(dvars, merge)
+
+  if merge.inherits then
+    local t = mod.index.name[merge.inherits]
+    if t and not inherited[t] then
+      mod.GetInherited(dvars, t, inherited)
+    end
+  end
+end
+
+local GetClassInfo, GetNumClasses, GetNumSpecializationsForClassID, GetSpecializationInfoForClassID = GetClassInfo, GetNumClasses, GetNumSpecializationsForClassID, GetSpecializationInfoForClassID
+mod.GetIndex = function()
+  print(cWord('**** Index Init'))
+
+  local index = mod.index
+  local globalIndex = index.global
+  local nameIndex = index.name
+  local tdb = TurokData.spirit.timers
+  local classID = {}
+  local className = {}
+  local classSpecID = {}
+  local specIDClass = {}
+
+  for id=1, GetNumClasses() do
+    local _, name = GetClassInfo(id)
+    classID[name] = id
+    className[id] = name
+    index[name] = setmetatable({}, index_mt)
+
+    classSpecID[id] = {}
+    for h=1, GetNumSpecializationsForClassID(id) do
+      local specID, specName = GetSpecializationInfoForClassID(id, h)
+      classSpecID[id][h] = specID
+      specIDClass[specID] = id
+    end
+  end
+
+  if not tdb then
+    print('Missing config table.')
+    return
+  end
+  --- Setup virtuals
+  for setID, entry in ipairs(tdb) do
+    if entry.virtual then
+      print('loading virtual set:', entry.name)
+      globalIndex[setID] = entry
+      nameIndex[entry.name or ('noname'.. setID)] = entry
+    end
+  end
+
+  --- Starting readin'
+  for setID, entry in ipairs(tdb) do
+    if not entry.virtual then
+
+      print('* .index.global['.. setID..'] =', entry.name)
+      local dvars = T.Config_Push({}, entry, nil, cKey('['..setID..']')..'.'..cWord('dvars'))
+      dvars.name = entry.name
+      globalIndex[setID] = dvars
+
+      local name = dvars.name or ('noname'..setID)
+      nameIndex[name] = entry
+      print('* .timersByName['..name..'] =', setID, entry.setID)
+
+      --- Combine with any inherited templates
+      if dvars.inherits and nameIndex[dvars.inherits] then
+        print('* Adding heritable data from', cText(dvars.inherits))
+        mod.GetInherited(dvars, nameIndex[dvars.inherits], {[dvars] = dvars})
+        --T.Config_Merge(dvars, nameIndex[dvars.inherits], dvars, cKey('['..setID..']')..'.'..cWord('dvars'))
+      end
+      -- class index
+      if not (dvars.playerClass or dvars.specID) then
+        for id, class in pairs(className) do
+          index[class][setID] = dvars
+        end
+        print('* indexed globally')
+      else
+        if dvars.playerClass then
+          if type(dvars.playerClass) ~= 'table' then
+            dvars.playerClass = {dvars.playerClass }
+          end
+          for i, playerClass in ipairs(dvars.playerClass) do
+            index[playerClass][setID] = dvars
+            print('* applying playerClass entry '..cNum(i)..': '..cWord(playerClass))
+          end
+        end
+
+        if dvars.specID then
+          if type(dvars.specID) ~= 'table' then
+            dvars.specID = {dvars.specID }
+          end
+          for i, specID in ipairs(dvars.specID) do
+            print('* applying globalSpecID entry '..cNum(i)..': '..cNum(specID)..' -> '..cWord(className[specIDClass[dvars.specID]]))
+            index[className[specIDClass[specID]]][setID] = dvars
+          end
+        end
+      end
+    end
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Timer.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,587 @@
+--- Turok - Timer/Timer.lua
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+--- Defines common elements for the various timer HUDs
+local ADDON, _A = ...
+local _G, CreateFrame, tconcat, GetInventoryItemsForSlot, GetInventoryItemID = _G, CreateFrame, table.concat, GetInventoryItemsForSlot, GetInventoryItemID
+local T, F, tostring, type, max, tinsert, unpack, UIParent, loadstring = _A.Addon, _A.LibFog, tostring, type, max, table.insert, unpack, _G.UIParent, loadstring
+local mod = T.modules.TimerControl
+local P = mod.prototype
+local db
+
+local pairs, ipairs, gsub, sub, setmetatable = pairs, ipairs, string.gsub, string.sub, setmetatable
+local INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2 =
+INVTYPE_FINGER, INVSLOT_FINGER1, INVSLOT_FINGER2, INVTYPE_TRINKET, INVSLOT_TRINKET1, INVSLOT_TRINKET2
+--@debug@
+local DEBUG = true
+--@end-debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if not DEBUG then return end
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Timer', ...)
+  end
+end
+
+local Timer_GetPrintHandler = function(self)
+  if self.trace then
+  return function(...)
+    print(...)
+    _G.print('TimerFocus', ...)
+  end else
+    return print
+  end
+end
+local pb_suppressed = {}
+
+function mod:OnInitialize()
+
+  --@debug@
+  TurokData.spirit.timers = Turok.defaults.spirit.timers
+  --@end-debug@
+  self.db = TurokData.spirit
+  db = self.db
+  self.active_cooldowns = {}
+  self.cast_units = {}
+  self.buff_units = {}
+  self.loaded_types = {}
+  self.loaded_triggers = {}
+  self.equipped = {}
+  self.containers = {}
+  self.timers = {} -- active timers
+  self.empty_frames = {} -- foster table for frames released by talent change
+
+
+  T:RegisterChatCommand("tsp", self.Import_Open)
+  T:RegisterChatCommand("tka", self.Dialog_Command)
+  T:RegisterChatCommand("tki", self.CreateIndex)
+  --T:Print("/tsp to import spells. /tka to open create dialog")
+  -- suppress cacophony from all cooldowns activating at login
+  self.quiet = true
+  --self:ScheduleTimer(function() self:Dialog_Command() end, 4)
+
+end
+
+local mt_single = {
+  __mode = "v",
+  __newindex = function(t,k,v)
+    rawset(t,k,v)
+    _G.print('DB', 'TCMeta: adding leaf', k, '=', v)
+  end}
+local mt_double = {
+  __index = function(t,k)
+    t[k] = setmetatable({}, mt_single)
+    _G.print('DB', 'TCMeta: add layer', k, '=', t[k])
+    return t[k]
+  end,
+  __newindex = function(t,k,v)
+    rawset(t,k,v)
+    _G.print('DB', 'TCMeta: adding to top layer', k, '=', v)
+  end
+}
+local mt_error = {
+  __call =function (t, txt)
+    t.disable = true
+    tinsert(t, txt)
+  end
+}
+
+--- Sets and cleans up index data used by event handlers
+local Timer_UpdateIndex = function(self, key)
+
+  -- Is there already an entry for this key/value?
+  if self.frames[key] then
+    local lim = #mod.frames[key]
+    --[[
+    for i = self.frames[key]+1, lim, 1 do
+      mod.frames[key][i] = mod.frames[key+1]
+    end]]
+    --self.frames[key] = nil
+    print('     ', cText('mod.frames.')..cWord(key), '=', #mod.frames[key])
+    print('     ', cText('self.frames.')..cWord(key), '=', cNum(self.frames[key]))
+  end
+
+  if key then
+    local i = #mod.frames[key]+1
+    --mod.frames[key][i] = self
+    self.frames[key] = i
+    print('     ', cText('self.frames.')..cWord(key), '=', #mod.frames[key])
+  end
+  mod.loaded_types[key] = (#mod.frames[key] == 0) and nil or true
+  print('     ',cText(key..'_is_loaded'), '=', cBool(mod.loaded_types[key]))
+end
+
+--- Loading initators
+function mod:OnEnable()
+  mod.LoadPresets()
+  mod.GetIndex()
+  -- setup indexes, use nested weak table for status since they implicitly have a key variable
+  mod.frames = {}
+  for class, p in pairs(mod.prototype.status) do
+    print('nested index table', class)
+    mod.frames[class] = setmetatable({}, mt_double)
+  end
+  mod.frames.spellName = setmetatable({}, mt_double)
+  for class, p in pairs(mod.prototype.display) do
+    mod.frames[class] = setmetatable({}, mt_single)
+  end
+  for class, p in pairs(mod.prototype.trigger) do
+    mod.frames[class] = setmetatable({}, mt_single)
+  end
+
+  local srcIndex = mod.timers
+  if T.playerClass and mod.index[T.playerClass] then
+    srcIndex = mod.index[T.playerClass]
+    print('*** Found index for '..tostring(T.playerClass)..', using that.')
+  else
+    print(cWord('*** Using global index.'))
+  end
+  mod.activeSpec = T.specID
+
+  --- go through that list
+  for id, timer in pairs(srcIndex) do
+    local result, message = mod:EnableTimer(id, timer)
+  end
+
+  mod.InitTimers()
+  --- Delay sound activations so there isn't a giant cacophony on load
+  mod:ScheduleTimer(function()
+    self.quiet = nil
+  end, db.audio_delay or 2)
+end
+
+function mod:EnableTimer(id, dvars)
+  local print = Timer_GetPrintHandler(dvars)
+  print('-{', cPink(dvars.name))
+  if not dvars then
+    if not mod.index.global[id] then
+      return false,  "Unable to resolve dvars table."
+    end
+    dvars = mod.index.global[id]
+  end
+  if dvars.virtual then
+    return
+  end
+
+  local spirit, newFrame = mod:GetTimer(id, dvars)
+  if not spirit then return spirit, newFrame end
+
+  local cvars = spirit.cvars
+  local dvars = spirit.dvars
+  local trigger = P.trigger[cvars.type]
+  local display = P.display[cvars.display]
+  local cvars = spirit.cvars
+  local index = mod.frames
+  local print = Timer_GetPrintHandler(cvars)
+
+  if spirit.disable then
+    return false, "Manually disabled." -- nothing to do, nothing to say
+  end
+
+  --- Interpret STATUS vars
+  print(cText('  *** Merging Status Data'))
+  spirit.disable = dvars.disable
+  local pcount = 1
+  for k, handler in pairs(P.status) do
+    if cvars[k] then
+      if handler.Init then
+        print(cWord('  * Firing ')..cKey(k)..cWord('.Init'), cNum(cvars[k]))
+        handler.Init(spirit, cvars[k])
+      else
+        print('   ', cText('skipped'), cKey(k))
+      end
+      pcount = pcount + 1
+    end
+  end
+
+  spirit.Event = trigger.Event
+  spirit.Value = trigger.Value
+  spirit.SetText = mod.SetText
+  spirit.LoadText = mod.LoadText
+  spirit.Query = trigger.Query
+  spirit.Set = trigger.Set
+
+  --- Display handler init
+  if display.Init then
+    print(cText('  * Display Init:'), cKey(dvars.display))
+    display.Init(spirit)
+  end
+
+  --- Trigger handler and events Load()
+  print(cText('  * Trigger Init:'), cKey(dvars.type))
+  trigger.Init(spirit)
+
+
+  if C_PetBattles.IsInBattle() then
+    spirit.disable = true
+    spirit.debug_info("Hidden for pet battle")
+    pb_suppressed[id] = true
+  end
+
+
+  if spirit.disable then
+    spirit:UnregisterAllEvents()
+    spirit.displayState = nil
+    spirit.prevState = nil
+    spirit:Hide()
+    return false, tconcat(spirit.debug_info,"\n")
+  else
+    print('--', self.disable and cPink('DISABLED') or cNum('ENABLED'), #spirit.debug_info > 0 and tconcat(spirit.debug_info,"\n"), '}')
+    return true, tconcat(spirit.debug_info,"\n")
+  end
+end
+
+function mod:GetTimer(id, dvars)
+  local print = Timer_GetPrintHandler(dvars)
+  local newFrame
+  if not mod.timers[id] then
+    print(cKey('  [[CreateTimer'))
+    newFrame = true
+    --- Compile the cvar table from the various config layers:
+     -- Start with timer dvars, overwritten by any container settings, then a disable check, then merge in prototype values
+    local cvars = T.Config_Push({}, dvars, nil, cKey('['..id..']')..'.'..cWord('cvars'))
+    cvars.name = dvars.name -- push function ignores name keys
+
+    if dvars.container and db.containers[dvars.container] then
+      print(cText('    * Merging Container overrides'))
+      T.Config_Push(cvars, db.containers[dvars.container], cvars, cKey('['..id..']')..'.'..cWord('cvars'))
+    end
+
+    --- Stop here if disabled via SavedVars
+    if cvars.disable then
+      return false, "Manually disabled"
+    end
+
+    --- Localize the stuff we are going to loop over
+    local display = P.display[cvars.display]
+    local trigger = P.trigger[cvars.type]
+    local displayType = cvars.display
+    local triggerType = cvars.type
+    if not (display and trigger) then
+      return nil, "Missing prototype data. Summary: "..tostring(displayType).."="..(display and 'OK' or 'MISSING') ..
+          " "..tostring(triggerType).."="..(trigger and 'OK' or 'MISSING')
+    end
+
+    --- Establish the order in which values are merged
+    print(cText('    * Merging object CVars'))
+    local cvar_class = {cWord('db.'..displayType), cWord('db.'..triggerType), cWord('db.global')}
+    local cvar_array = {
+      db[displayType],
+      db[triggerType],
+      db.global,
+    }
+    local override_class = {cWord('trigger.'..cvars.type), cWord('display.'.. cvars.display)}
+    local override_array = {
+      display.cvars,
+      trigger.cvars }
+
+    --- Table merge user settings
+    for i, p in pairs(cvar_array) do
+      print('    '..cNum(i)..' merge ['..cvar_class[i]..']')
+      T.Config_Merge(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars'))
+    end
+
+    --- Overwrite with anything defined by the prototype structure because it's important
+    local _, odiff
+    for i, p in ipairs(override_array) do
+      _, odiff = T.Config_Push(cvars, p, cvars, cKey('['..id..']')..'.'..cWord('cvars'))
+    end
+    local print = Timer_GetPrintHandler(cvars)
+
+    --- Create the UI frame and seed it with the data we just composed
+    local spirit =  CreateFrame('Frame', 'TurokTimerFrame'..gsub(dvars.name, "[^%a%d]", ''), UIParent, display.inherits)
+    spirit.trace = cvars.trace
+    spirit.timerID = id
+    spirit.timerName = dvars.name
+    spirit.container = dvars.container
+    spirit.cvars = cvars
+    spirit.dvars = dvars
+    spirit.Update = display.Update
+    spirit.SetState = display.SetState
+    spirit.Report = mod.Report
+    spirit.Stats = trigger.Stats
+
+    --- Set Layout Statics
+    T.SetFrameLayout(spirit, cvars)
+
+    --- Create troubleshooting collection
+    spirit.debug_info = setmetatable({}, mt_error)
+
+    --- Add the frame to corresponding prototype indexes
+    spirit.frames = {}
+    spirit.events = {}
+
+    if spirit.display ~= displayType then
+      spirit.display = displayType
+      Timer_UpdateIndex(spirit, displayType)
+    end
+    if spirit.type ~= triggerType then
+      spirit.type = triggerType
+      Timer_UpdateIndex(spirit, triggerType)
+    end
+    --- Add the frame to global index
+    mod.timers[id] = spirit
+  end
+
+  return mod.timers[id], newFrame
+end
+
+function mod.InitTimers()
+  local print = function(...) _G.print('TimerEvent', ...) end
+  print('INIT TIMERS ====================')
+  for id, spirit in pairs(mod.timers) do
+    if spirit.disable then
+      print(id, 'disabled:', tconcat(spirit.debug_info or {}, ', '))
+    else
+
+    print(cText('init'), cNum(id), cWord(spirit.name))
+    --- Throw a dry event to initialize values
+    print(cText(' *'), cWord('prototype.'..cKey(spirit.dvars.type)..'.'..cWord('Load')))
+    P.trigger[spirit.dvars.type].Event(spirit)
+
+    --- Set loose
+    print(cText(' *'), cWord('prototype')..'.'..cKey('events')..'.'..cWord('Load'))
+    mod.UpdateEvents(spirit, P.trigger[spirit.dvars.type].events)
+    end
+  end
+  print('INIT DONE =========================')
+end
+
+function mod:DisableTimer(name, timer)
+  local timer_frame = mod.db.timers[name]
+  if timer_frame and not timer_frame.disable then
+    timer_frame.disable = true
+    timer_frame:UnregisterAllEvents()
+    timer_frame:Hide()
+  end
+end
+
+function mod.UpdateEvents(self, events)
+  local print = Timer_GetPrintHandler(self)
+
+  self:SetScript('OnEvent', nil)
+  self:UnregisterAllEvents()
+
+  local proxy, listen = {}, {}
+  for event, handler in pairs(events) do
+    if mod[event] then
+      tinsert(proxy, cNum(event))
+    else
+      tinsert(listen, cWord(event))
+      self:RegisterEvent(event)
+    end
+    self.events[event] = handler
+  end
+
+  if #proxy > 0 then
+    print( '  -', cKey(self.name), cWord('receiving'), tconcat(proxy, ', '))
+  end
+  if #listen > 0 then
+    print( '  -', cKey(self.name), cText('listening'), tconcat(listen, ', '))
+  end
+
+  self:SetScript('OnEvent', self.Event)
+end
+
+local match_sub = {
+  {'%%c', "' .. tostring(t.caster).. '"},
+  {'%%h', "' .. tostring((t.valueFull >= 60) and (math.floor(t.valueFull/60)) or t.value) .. '"},
+  {'%%i', "' .. tostring((t.valueFull >= 60) and (t.value % 60) or ((t.valueFull < 6) and math.floor((t.ValueFull * 100) % 100) or '')) .. '"},
+  {'%%n', "' .. tostring(t.spellName) .. '"},
+  {'%%p', "' .. tostring(t.value) .. '"},
+  {'%%d', "' .. tostring(t.chargeDuration or t.duration) .. '"},
+  {'%%%.p', "' .. string.sub(tostring((t.valueFull %% 1) * 100),0,1) .. '"},
+  {"%%s", "' .. (t.stacks or t.charges or '') .. '"},
+}
+
+-- dot syntax implies use as embedded method
+function mod.LoadText(self)
+  print(cKey('parsing textRegions for'), self.timerName, self.timerID)
+  self.textTypes = {}
+  self.textValues = {}
+  for name, region in pairs(self.textRegions) do
+    print('  ', cWord('textRegions')..'["'.. cType(self.timerName)..'"].'..cType(name))
+    if self.cvars[name..'Text'] then
+
+      -- todo: collect match counts and index the text fields by match types
+      local str = self.cvars[name..'Text']
+      for i, args in ipairs(match_sub) do
+        if str:match(args[1]) then
+          if not self.textTypes[args[1]] then
+            self.textTypes[args[1]] = {}
+          end
+          tinsert(self.textTypes[args[1]], region)
+          str = str:gsub(args[1], args[2])
+        end
+      end
+      str = "local t = _G.Turok.modules.TimerControl.timers["..self.timerID.."]\n"
+          .. "\n return '" .. str .. "'"
+      local func = assert(loadstring(str))
+      self.textValues[name] = func
+    end
+  end
+
+  --mod.SetText(self)
+end
+
+--- generic text setter
+local HIDDEN, PASSIVE, ACTIVE = 0, 1, 2
+mod.SetText = function(self)
+  if self.displayState ~= ACTIVE then
+    for name, region in pairs(self.textRegions) do
+      region:SetText(nil)
+    end
+    return
+  end
+
+  if not self.textValues then
+    self.textValues = {}
+    mod.LoadText(self, self.cvars)
+  end
+
+  -- hide when above a certain number
+
+  if self.spiral and self.spiral.subCounter then
+    if self.valueFull > 6 then
+      if self.textValues.subCounter then
+        --print('hiding milliseconds')
+        self.textRegions.subCounter:Hide()
+        self.textRegionsSub = self.textRegions.subCounter
+        self.textValuesSub = self.textValues.subCounter
+        self.textRegions.subCounter = nil
+        self.textValues.subCounter = nil
+      end
+    else
+      if not self.textValues.subCounter then
+        --print('showing milliseconds')
+        self.textValues.subCounter = self.textValuesSub
+        self.textRegions.subCounter = self.textRegionsSub
+        self.textRegions.subCounter:Show()
+      end
+    end
+  end
+
+  for name, region in pairs(self.textRegions) do
+    --print(name)
+    --print(name, self.timerName, self.textValues[name](self))
+    region:SetText(self.textValues[name](self))
+  end
+end
+
+
+-------------------------------------------------------------------------
+--- Second-tier handlers to cut down on the number of Status:Event() polls
+
+--- UNIT_SPELLCAST_*** use args to filter out the number of full handler runs
+function mod:UNIT_SPELLCAST_SUCCEEDED (e, unit, spellName, rank, castID, spellID)
+  if not mod.frames.unit[unit] then
+    return
+  end
+
+  if #mod.frames.spellName[spellName] > 0 then
+    print('spellName-ID relation detected:', cWord(spellName), cNum(spellID))
+    for i, frame in pairs(mod.frames.spellName[spellName]) do
+      if not frame.frames.spellID then
+        frame.frames.spellID = {}
+      end
+      if not frame.frames.spellID[spellID] then
+
+        tinsert(mod.frames.spellID[spellID], frame)
+        frame.frames.spellID[spellID] = #mod.frames.spellID[spellID]
+        print(cText('  updating'), cKey(frame.timerName))
+      end
+    end
+    mod.frames.spellName[spellName] = nil
+  end
+
+
+
+  if mod.frames.spellID[spellID] then
+    for i, timer_frame in pairs(mod.frames.spellID[spellID]) do
+      print(cText('caught spell'), cWord(spellName), 'for', timer_frame:GetName())
+      timer_frame:Event(e, unit, spellName, rank, castID, spellID)
+    end
+  end
+end
+mod.UNIT_SPELLCAST_CHANNEL_START = mod.UNIT_SPELLCAST_SUCCEEDED
+
+--- Fire a dry event to force status updates on units with changing GUID's
+function mod:PLAYER_TARGET_CHANGED(e, unit)
+  print('doing a target swap thing')
+  for k, v in pairs( self.frames.unit.target) do
+    print(k, v)
+    v:Event(nil, 'target')
+  end
+end
+
+--- Same thing but for talent/spec-driven
+function mod:PLAYER_TALENT_UPDATE(e, unit)
+  print('')
+  print('')
+  print(cText(e), T.specPage, T.specName)
+
+  local update_queue = {}
+  for _, k in ipairs({'talentID', 'talentRow', 'specPage'}) do
+    for value, frameSet in pairs(mod.frames.talentID) do
+      for id, frame in ipairs(frameSet) do
+        print(frame.timerID, frame.timerName)
+        update_queue[frame.timerID] = frame
+      end
+    end
+  end
+
+  for id, frame in pairs(update_queue) do
+    print('Refreshing spec-related frames', id, frame.timerName)
+    frame.disable = nil
+    table.wipe(frame.debug_info)
+    local res, msg = mod:EnableTimer(id, frame.dvars)
+  end
+
+end
+
+function mod:PLAYER_EQUIPMENT_CHANGED(e, slot, hasItem)
+  print(e, slot, hasItem)
+  local itemCheckList
+  if mod.frames.inventoryID and  mod.frames.inventoryID[slot] then
+    print('  Inventory Frames:')
+      itemCheckList = GetInventoryItemsForSlot(slot, {}, false)
+       for id, slotFrame in pairs(mod.frames.inventoryID[slot]) do
+        print('   * Updating', cNum(id), cWord(slotFrame.timerName))
+        local res, msg = mod:EnableTimer(slotFrame.timerID, slotFrame.dvars)
+        print('     ', cBool(res), cText(msg))
+      end
+  end
+  if itemCheckList then
+    print(unpack(itemCheckList))
+  end
+  local itemID = GetInventoryItemID('player', slot)
+  if itemID and mod.frames.itemID[itemID] then
+    print('  Item ID Frames:')
+    for id, itemFrame in pairs(mod.frames.itemID[itemID]) do
+      print('   * Updating', cNum(id), cWord(itemFrame.timerName))
+
+      local res, msg = mod:EnableTimer(itemFrame.timerID, itemFrame.dvars)
+      print('     ', cBool(res), cText(msg))
+    end
+  end
+end
+function mod:PET_BATTLE_OPENING_START ()
+  for i, v in pairs(mod.timers) do
+    if not v.disable then
+      print('suppressing', v:GetName())
+      v.disable = true
+      v:Hide()
+      pb_suppressed[i] = true
+    end
+  end
+end
+function mod:PET_BATTLE_CLOSE()
+  for id, v in pairs(mod.timers) do
+    if pb_suppressed[id] then
+      print('restoring', v:GetName())
+      mod:EnableTimer(id)
+      pb_suppressed[id] = nil
+    end
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Timer/Timer.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,327 @@
+<!--
+  Timer-specific XML defs
+-->
+<Ui>
+  <Script file="Timer.Init.lua" />
+  <Script file="Presets.lua" />
+  <Script file="Timer.lua" />
+  <Script file="Container.lua" />
+  <Script file="Status.lua" />
+  <Script file="Aura.lua" />
+  <Script file="Cooldown.lua" />
+  <Script file="Icon.lua" />
+  <Script file="Progressbar.lua" />
+  <Script file="Import.lua" />
+  <Script file="Editor.lua" />
+
+
+  <!-- EASY TIMER FRAME -->
+
+  <Frame name="TurokTimerScripts"  parent="UIParent" virtual="true" hidden="true">
+    <Scripts>
+      <OnLoad>
+        self.textRegions = {}
+      </OnLoad>
+      <OnShow>
+        self:Report()
+      </OnShow>
+      <OnHide>
+        self:Report()
+      </OnHide>
+      <OnUpdate>
+        self:Update()
+      </OnUpdate>
+    </Scripts>
+    <Size x="64" y="64" />
+    <Anchors>
+      <Anchor point="CENTER" />
+    </Anchors>
+    <Animations>
+      <AnimationGroup parentKey="iconIntro" looping="NONE" ignoreFramerateThrottle="true">
+        <Scale childKey="icon" order="1" duration=".10" fromScaleX="0.1" toScaleX="1.0" fromScaleY=".1" toScaleY="1" />
+      </AnimationGroup>
+      <AnimationGroup parentKey="Intro" looping="NONE" ignoreFramerateThrottle="true">
+        <Alpha childKey="foreground" order="1" duration=".5" fromAlpha="0" toAlpha="1" />
+        <Alpha childKey="background" order="1" duration=".5" fromAlpha="0" toAlpha="1" />
+        <Scripts>
+          <OnPlay>
+            local g = self:GetParent()
+            g.collected = nil
+            g.add = true
+            g:Report()
+            --@debug@
+            print('Layout', g.cvars.type, 'Intro |cFFFFFF00START', g:GetName())--@end-debug@
+            g:Show()
+            if g.cvars.sound_active then
+              PlaySoundFile(g.cvars.sound_active)
+            end
+            if g.icon then
+              g.iconIntro:Play()
+            end
+          </OnPlay>
+          <OnStop>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout', g.cvars.type, 'Intro |cFFFF4400STOP', g:GetName())--@end-debug@
+            if g.enableIcon then
+              g.iconIntro:Stop()
+            end
+          </OnStop>
+          <OnFinished>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout', g.cvars.type, 'Intro |cFF00FF00FINISH', g:GetName())--@end-debug@
+            if g.enableIcon then
+              g.iconIntro:Stop()
+            end
+            g:UpdateAlpha(Turok.inCombat, g.displayState, g.fillState)
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup parentKey="iconOutro" looping="NONE" ignoreFramerateThrottle="true">
+        <Scale childKey="icon" order="1" duration=".10" fromScaleX="1" toScaleX="0.1" fromScaleY="1" toScaleY="0.1"/>
+      </AnimationGroup>
+      <AnimationGroup parentKey="Outro" looping="NONE" ignoreFramerateThrottle="true">
+        <Alpha childKey="foreground" order="1" duration=".5" change="-1" />
+        <Alpha childKey="background" order="1" duration=".5" change="-1" />
+        <Scripts>
+          <OnPlay>
+            local g = self:GetParent()
+            g.collected = nil
+            g.trash = true
+            g:Report()
+            --@debug@
+            print('Layout', g.cvars.type, '|cFF0088FFOutro |cFFFFFF00START', g:GetName())--@end-debug@
+            if g.spiral then
+              g.spiral:StopAnimating()
+              g.spiral:Hide()
+            end
+            if g.cvars.sound_hidden then
+              PlaySoundFile(g.cvars.sound_hidden)
+            end
+            if g.enableIcon then
+              g.iconOutro:Play()
+            end
+          </OnPlay>
+          <OnStop>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout', g.cvars.type, '|cFF0088FFOutro |cFFFF4400STOP', g:GetName())--@end-debug@
+            if g.enableIcon then
+              g.iconOutro:Stop()
+            end
+          </OnStop>
+          <OnFinished>
+            local g = self:GetParent()
+            g.trash = false
+            --@debug@
+            print('Layout', g.cvars.type, '|cFF0088FFOutro |cFF00FF00Finish', g:GetName())--@end-debug@
+            if g.enableIcon then
+              g.iconOutro:Stop()
+            end
+            g:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup parentKey="Retro" looping="NONE" ignoreFramerateThrottle="true">
+        <Alpha childKey="iconFlash" fromAlpha="0" toAlpha=".7" duration="0.05" order="1" />
+        <Alpha childKey="iconFlash" fromAlpha=".7" toAlpha="0" duration="0.15" order="2" />
+        <Scripts>
+          <OnPlay>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout', g.cvars.type, '|cFFFFFF00Retro |cFFFFFF00START', g:GetName())--@end-debug@
+            if g.iconFlash then
+              g.iconFlash:Show()
+            end
+          </OnPlay>
+          <OnStop>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout',g.cvars.type, '|cFFFFFF00Retro |cFFFF4400STOP', self:GetParent():GetName())--@end-debug@
+          </OnStop>
+          <OnFinished>
+            local g = self:GetParent()
+            --@debug@
+            print('Layout',g.cvars.type, '|cFFFFFF00Retro |cFF00FF00FINISH', g:GetName())--@end-debug@
+            if g.iconFlash then
+              g.iconFlash:Hide()
+            end
+            g:UpdateAlpha(Turok.inCombat, g.displayState, g.fillState)
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup name="Slider" parentKey="slide" ignoreFramerateThrottle="true" looping="NONE">
+        <Translation parentKey="t1" duration="0.11" order="1" />
+      </AnimationGroup>
+    </Animations>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture file="Interface\ICONS\INV_Misc_QuestionMark" name="$parentSpellIcon" parentKey="icon" textureSubLevel="-7" hidden="true">
+          <TexCoords top="0.1" left="0.1" bottom="0.9" right="0.9" />
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture parentKey="iconFlash" name="$parentIconFlashBox" hidden="true">
+          <Anchor point="TOPLEFT" relativeKey="icon"/>
+          <Anchor point="BOTTOMRIGHT" relativeKey="icon" />
+          <Color r="1" g="1" b="1" a="1" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <Texture parentKey="debugPanel" name="$parentDebugWindow" hidden="true">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" />
+            <Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT" x="0" y="-40" />
+          </Anchors>
+        </Texture>
+        <FontString inherits="TurokFontDetail" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="TurokIconTemplate" parent="UIParent" virtual="true" hidden="true" inherits="TurokTimerScripts">
+    <Scripts>
+      <OnLoad inherit="prepend">
+        -- not using parentArray because we want name info
+        self.textRegions = {
+          counter = self.spiral.counter,
+          subCounter = self.spiral.subCounter,
+          charges = self.spiral.charges
+        }
+      </OnLoad>
+    </Scripts>
+    <Frames>
+      <Cooldown name="$parentCooldownSpiral" parentKey="spiral" inherits="CooldownFrameTemplate">
+        <SwipeTexture parentKey="spiralTex">
+          <Color r="0" g="0" b="0" a="0.6" />
+        </SwipeTexture>
+        <Anchors>
+          <Anchor relativeKey="$parent.icon" point="TOPLEFT" relativePoint="TOPLEFT" x="0" y="0" />
+          <Anchor relativeKey="$parent.icon" point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT" x="0" y="0" />
+        </Anchors>
+        <Layers>
+          <Layer level="OVERLAY">
+            <FontString inherits="TurokFontMed" name="$parentCounter" parentKey="counter" justifyH="RIGHT" justifyV="BOTTOM">
+              <KeyValues>
+                <!-- [1] below 6 seconds remaining -->
+                <KeyValue key="anchor1" value="TOP" />
+                <KeyValue key="anchor1_rel" value="TOP" />
+                <!-- [2] default -->
+                <KeyValue key="anchor2" value="TOP" />
+                <KeyValue key="anchor2_rel" value="TOP" />
+                <!-- [3] > 100 sec remaining -->
+                <KeyValue key="anchor3" value="TOPRIGHT" />
+                <KeyValue key="anchor3_rel" value="TOP" />
+              </KeyValues>
+              <Size x="40" y="40" />
+              <Anchors>
+                <Anchor point="TOPRIGHT" relativePoint="TOP" relativeKey="$parent.$parent.icon" x="0" y="4" />
+              </Anchors>
+            </FontString>
+            <FontString inherits="TurokFontDetail" name="$parentSubCounter" parentKey="subCounter" justifyH="LEFT" text="subtext" justifyV="BOTTOM">
+              <KeyValues>
+                <KeyValue key="anchor1" value="LEFT" />
+                <KeyValue key="anchor1_rel" value="RIGHT" />
+                <KeyValue key="anchor2" value="LEFT" />
+                <KeyValue key="anchor2_rel" value="RIGHT" />
+                <KeyValue key="anchor3" value="LEFT" />
+                <KeyValue key="anchor3_rel" value="RIGHT" />
+              </KeyValues>
+              <Size x="40" y="40" />
+              <Color r="1" g="1" b="0" a="1" />
+              <Anchors>
+                <Anchor point="LEFT" relativePoint="RIGHT" relativeKey="$parent.counter" x="0" y="1" />
+              </Anchors>
+            </FontString>
+            <FontString inherits="TurokFontDetail" name="$parentCharges" parentKey="charges" justifyH="RIGHT" justifyV="BOTTOM" text="charges">
+              <Size x="80" y="30" />
+              <Color a="1" r="1" g="1" b="0" />
+              <Anchors>
+                <Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT" />
+              </Anchors>
+            </FontString>
+          </Layer>
+        </Layers>
+      </Cooldown>
+    </Frames>
+  </Frame>
+
+  <Frame name="TurokProgressbarTemplate" parent="UIParent" virtual="true" hidden="true" inherits="TurokTimerScripts">
+    <Scripts>
+      <OnLoad inherit="prepend">
+        self.textRegions = {
+          left = self.left,
+          right = self.right
+        }
+      </OnLoad>
+    </Scripts>
+    <Layers>
+
+      <Layer level="BACKGROUND">
+
+
+        <Texture name="$parentProgressBackground" parentKey="background" textureSubLevel="0" />
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture name="$parentProgressForeground" parentKey="foreground" textureSubLevel="1" />
+      </Layer>
+
+      <Layer level="OVERLAY">
+        <FontString inherits="TurokFontDetail" name="$parentLeftText" parentKey="left" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="LEFT" />
+          </Anchors>
+        </FontString>
+        <FontString inherits="TurokFontDetail" name="$parentRightText" parentKey="right" justifyH="RIGHT">
+          <Anchors>
+            <Anchor point="RIGHT" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <!-- Collector Tray -->
+  <Frame name="TkContainerTemplate" virtual="true" parent="UIParent" alpha="1" movable="true">
+    <Size x="700" y="200" />
+    <Scripts>
+      <OnLoad>
+        self:RegisterForDrag('LeftButton')
+        self:EnableMouse(false) -- for now
+      </OnLoad>
+      <OnDragStart>
+        if self:GetEnableMouse() then
+          self.cx = self:GetLeft()
+          self.cy = self:GetTop()
+          self:StartMoving()
+        end
+      </OnDragStart>
+      <OnDragStop>
+        self:StopMovingOrSizing()
+        self.x = self.x + (self:GetLeft() - self.cx)
+        self.y = self.y + (self:GetTop() - self.cy)
+        self:SetPoint(self.anchor, self.parent, self.anchorTo, self.x, self.y)
+      </OnDragStop>
+    </Scripts>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture name="$parentBackdrop" parentKey="ConfigBG" setAllPoints="true" hidden="true">
+          <Color r="1" g="1" b="1" a="0.25" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentNameTag" parentKey="NameText" inherits="TurokFont" justifyH="LEFT" justifyV="BOTTOM" hidden="true">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" x="0" y="2" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/Chat.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,8 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 2/19/2016 9:00 AM
+local ADDON, env = ...
+local mod = Turok:NewModule("Chat")
+setfenv(0, env)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/PetBattle.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,288 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 1/17/2016 6:51 PM
+
+local ADDON, Turok = ...
+local T, _G, pairs, pairs = Turok.Addon, _G, pairs, ipairs
+local mod = T:NewModule("PetBattle")
+local print = function(...) _G.print('Petz', ...) end
+local trace = function(self, e, ...)
+  print(e, ...)
+end
+
+local PBS
+local turn_font = [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf]]
+local turn_size = 24
+local turn_outline = 'OUTLINE'
+local icon_size = 64
+local spacing = 5
+local padding = 0
+local strongweak_size = 20
+
+local cpb = C_PetBattles
+T.defaults.petbattle = {
+  swatch = {
+    anchor = 'BOTTOM', anchorTo = 'BOTTOM',
+    parent = 'UIParent',
+    x = 0, y =240,
+    width = 200, height = 100,
+    font = [[Interface\Addons\Turok\Media\font\ArchivoNarrow-Bold.ttf]],
+    size = 16,
+  },
+  spell = {
+    anchor = 'TOPLEFT', anchorTo = 'TOPLEFT',
+    size = 24,
+    width = 72, height = 72,
+    x = 0, y =0,
+    padding = 0,
+    spacing = 0,
+  },
+  sideSwatch = {
+    width = 100, height = 50, padding = 0, spacing = 5,
+    anchor = 'TOPLEFT', anchorTo = 'BOTTOMLEFT', x = 0, y = 0,
+    parent = 'TkPetSwatch',
+  },
+  sideSpell = {
+    anchor = 'TOPLEFT', anchorTo = 'TOPLEFT',
+    size = 16,
+    width = 36, height = 36,
+    padding = 0,
+    spacing = 0,
+  },
+  icon = {
+    size = 72,
+    width = 64,
+    height = 64,
+  }
+}
+mod.abilityButtons = {}
+mod.switcherButtons = {}
+mod.icons = {}
+mod.strongweak = {}
+mod.turns = {}
+mod.petlist = {}
+local PLAYER, ENEMY = 1, 2
+local function PBS_UpdateEnemySwatch(self)
+
+  print('UpdateSwatch')
+  local db = TurokData.petbattle.swatch
+  local playerActivePet = cpb.GetActivePet(PLAYER)
+  local enemyActivePet = cpb.GetActivePet(ENEMY)
+  local enemyNumPets = cpb.GetNumPets(ENEMY)
+  local playerPetType = cpb.GetPetType(PLAYER, playerActivePet)
+  local enemyPetType = cpb.GetPetType(ENEMY, enemyActivePet)
+  local enemyPetName = cpb.GetName(ENEMY, playerActivePet)
+  for petSlot=1, 3 do
+    local swatchFrame = (petSlot == enemyActivePet) and self or ((petSlot < enemyNumPets) and self.sideSwatch or self.sideSwatch2)
+    local db = (swatchFrame == self) and db.spell or db.sideSpell
+    local type = cpb.GetPetType(ENEMY, petSlot)
+    print(cText('  PetType='), enemyActivePet)
+    print(cText('  current='), enemyPetType)
+    for spellSlot=1, 3 do
+      print('    ', cText('PetSlotOffset='), cNum(petSlot), cText('AbilitySlotOffset'), cNum(spellSlot))
+      local id, name, texture, cooldown, desc, numTurns, attackType, noStrongWeakHints = cpb.GetAbilityInfo(ENEMY, petSlot, spellSlot)
+      local usable, remaining = cpb.GetAbilityState(ENEMY, petSlot, spellSlot)
+
+      --print('info', cpb.GetAbilityInfo(ENEMY, petSlot, spellSlot))
+      -- print('effect', cpb.GetAbilityEffectInfo(id, turnIdx, effectIdx, paramName)
+      --print('state', cpb.GetAbilityState(ENEMY,petSlot,spellSlot))
+      --print('statemod', cpb.GetAbilityStateModification(ENEMY,petSlot,spellSlot))
+      --print('proc', cpb.GetAbilityProcTurnIndex(ENEMY,petSlot,spellSlot))
+      --print('statemod', cpb.GetAbilityState(ENEMY,petSlot,spellSlot))
+      local AbilityButton, icon, strong, turns -- Icon, Strength/Weakness, Turns cooldown
+      if not swatchFrame.buttons[spellSlot] then
+        swatchFrame.buttons[spellSlot] = CreateFrame('Frame', 'TkPetSpell'..spellSlot, swatchFrame, 'TkPetSpellTemplate')
+        AbilityButton = swatchFrame.buttons[spellSlot]
+
+        AbilityButton:SetSize(db.width, db.height)
+        AbilityButton:SetPoint(db.anchor, swatchFrame, db.anchor, (spellSlot-1)*(db.width+ db.spacing), 0)
+
+        AbilityButton.icon:SetSize(db.width, db.height)
+        AbilityButton.icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
+
+        AbilityButton.strongWeakHint:SetSize(strongweak_size, strongweak_size)
+        AbilityButton.strongWeakHint:SetPoint('CENTER', swatchFrame.buttons[spellSlot], 'CENTER', 0, 0)
+
+        AbilityButton.turnsLeft:SetFont(turn_font, turn_size, turn_outline)
+        AbilityButton.turnsLeft:SetPoint('CENTER', icon, 'CENTER')
+      else
+        AbilityButton = swatchFrame.buttons[spellSlot]
+      end
+      local icon, strong , turns = AbilityButton.icon, AbilityButton.strongWeakHint, AbilityButton.turnsLeft
+
+      print('Working on:', swatchFrame, AbilityButton)
+      print('AbilityState('..petSlot..', '..spellSlot..')', 'usable=', usable, 'turnsLeft=', remaining)
+      print(cWord(name), cKey(attackType))
+
+      if petSlot > enemyNumPets then
+        -- this will also eval true when pet battle over since numPets will be 0
+        AbilityButton.popOut:Play()
+      else
+        local abilityChanged
+        if id ~= AbilityButton.spellID then
+          abilityChanged = true
+          AbilityButton.spellID = id
+          AbilityButton.spellName = name
+          AbilityButton.spellDesc = desc
+          AbilityButton.spellType = attackType
+          AbilityButton.spellMaxCooldown = cooldown
+          AbilityButton.spellDesc = desc
+          AbilityButton.spellCooldown = remaining
+          AbilityButton.spellNumTurns = numTurns
+        end
+        if AbilityButton.petType ~= enemyPetType then
+          AbilityButton.petType = enemyPetType
+          AbilityButton.noStrongWeakHints = noStrongWeakHints
+        end
+
+        if not usable then
+          icon:SetDesaturated(true)
+          icon:SetVertexColor(0.5, 0.5, 0.5, 1)
+        else
+          icon:SetDesaturated(false)
+          icon:SetVertexColor(1, 1, 1, 1)
+        end
+
+        icon:SetTexture(texture)
+        turns:SetText((cooldown > 0) and cooldown or nil)
+        --AbilityButton.damage:SetText()
+
+        print('noStrongWeakHints', noStrongWeakHints)
+        print('abilityModification', cpb.GetAttackModifier(enemyPetType, playerPetType))
+        local modifier = cpb.GetAttackModifier(enemyPetType, playerPetType)
+        if not noStrongWeakHints  then
+          if modifier < 1 then
+            strong:SetTexture(0,1,0,0.5)
+          else
+            strong:SetTexture(1,0,0,0.5)
+          end
+
+          strong:Show()
+        else
+          strong:Hide()
+        end
+        if numTurns then
+          print('numTurns=',numTurns)
+        end
+        if not AbilityButton:IsVisible() or abilityChanged then
+          AbilityButton:Show()
+          AbilityButton.popIn:Play()
+        end
+
+      end
+
+
+    end
+    swatchFrame:SetSize(db.width*3+db.spacing*2+db.padding*2, db.height+padding*2)
+    swatchFrame.petName = enemyPetName
+    swatchFrame.petType = enemyPetType
+    local db = (swatchFrame == self) and db.swatch or db.sideSwatch
+    swatchFrame:Show()
+  end
+end
+
+local function PBS_Switch ()
+  print('Switch button was clicked.')
+end
+mod.PLAYER_ENTERING_WORLD = function(self, e)
+  print(cpb.IsInBattle())
+  if cpb.IsInBattle() then
+    PBS_UpdateEnemySwatch(PBS, e, 2)
+  end
+
+
+  if _G.PetBattleFrame.BottomFrame.SwitchPetButton then
+    print("There's a pet battle frame button ")
+    --_G.PetBattleFrame.BottomFrame.SwitchPetButton:SetScript('OnClick', PBS_Switch)
+  end
+end
+
+local PBS_Hide = function()
+  for i = 1, 3 do
+    if PBS.buttons[i] then
+      PBS.buttons[i].popOut:Play()
+    end
+    if PBS.sideSwatch.buttons[i] then
+      PBS.sideSwatch.buttons[i].popOut:Play()
+    end
+    if PBS.sideSwatch2.buttons[i] then
+      PBS.sideSwatch2.buttons[i].popOut:Play()
+    end
+  end
+end
+
+local PBS_Event = function(self, e, ...)
+  print('event', e)
+  local owner = ...
+  if e == 'PET_BATTLE_PET_ROUND_PLAYBACK_COMPLETE' or e == 'PET_BATTLE_OPENING_DONE' then
+    PBS_UpdateEnemySwatch(PBS)
+  elseif e == 'PET_BATTLE_CLOSE' then
+    PBS_Hide(PBS)
+  end
+end
+function mod:OnEnable()
+  PBS = CreateFrame('Frame', 'TkPetSwatch', UIParent, 'TkPetSwatchTemplate')
+  PBS.buttons = {}
+  PBS.sideSwatch = CreateFrame('Frame', 'TkSideSwatch', PBS, 'TkPetSwatchTemplate')
+  PBS.sideSwatch.buttons = {}
+  PBS.sideSwatch2 = CreateFrame('Frame', 'TkSideSwatch', PBS, 'TkPetSwatchTemplate')
+  PBS.sideSwatch2.buttons = {}
+  local db = TurokData.petbattle
+  T.SetFrameLayout(PBS, db.swatch)
+  T.SetFrameLayout(PBS.sideSwatch, db.sideSwatch)
+  T.SetFrameLayout(PBS.sideSwatch2, db.sideSwatch)
+  PBS.sideSwatch2:ClearAllPoints()
+  PBS.sideSwatch2:SetPoint('LEFT', PBS.sideSwatch, 'RIGHT', db.sideSpell.spacing, 0)
+
+  mod.effectIndex = {
+    cpb.GetAllEffectNames()
+  }
+
+  PBS:SetScript('OnEvent', PBS_Event)
+  PBS:RegisterEvent('PET_BATTLE_OPENING_DONE')
+  PBS:RegisterEvent('PET_BATTLE_PET_CHANGED')
+  PBS:RegisterEvent('PET_BATTLE_OVER')
+  PBS:RegisterEvent('PET_BATTLE_CLOSE')
+  PBS:RegisterEvent('PET_BATTLE_ABILITY_CHANGED')
+  PBS:RegisterEvent('PET_BATTLE_ACTION_SELECTED')
+  --PBS:RegisterEvent('PET_BATTLE_AURA_APPLIED')
+  --PBS:RegisterEvent('PET_BATTLE_AURA_CANCELED')
+  --PBS:RegisterEvent('PET_BATTLE_PET_TYPE_CHANGED')
+  PBS:RegisterEvent('PET_BATTLE_TURN_STARTED')
+  --PBS:RegisterEvent('PET_BATTLE_HEALTH_CHANGED')
+  --PBS:RegisterEvent('PET_BATTLE_MAX_HEALTH_CHANGED')
+  PBS:RegisterEvent('PET_BATTLE_PET_ROUND_RESULTS')
+  PBS:RegisterEvent('PET_BATTLE_PET_ROUND_PLAYBACK_COMPLETE')
+end
+
+--[[
+mod.PET_BATTLE_OPENING_DONE	 = trace
+mod.PET_BATTLE_OPENING_START = trace
+mod.PET_BATTLE_OVER	 = trace
+mod.PET_BATTLE_ABILITY_CHANGED = trace
+mod.PET_BATTLE_ACTION_SELECTED = trace
+mod.PET_BATTLE_AURA_APPLIED = trace  -- <team> <slot> <id>
+mod.PET_BATTLE_AURA_CANCELED = trace -- <team> <slot> <id>
+mod.PET_BATTLE_AURA_CHANGED = trace -- <team> <slot> <id>
+mod.PET_BATTLE_CAPTURED = trace
+mod.PET_BATTLE_CLOSE = trace
+mod.PET_BATTLE_FINAL_ROUND = trace -- <team>
+mod.PET_BATTLE_HEALTH_CHANGED = trace -- <team> <pet slot> <delta>
+mod.PET_BATTLE_LEVEL_CHANGED = trace
+mod.PET_BATTLE_LOOT_RECEIVED = trace
+mod.PET_BATTLE_MAX_HEALTH_CHANGED = trace
+mod.PET_BATTLE_PET_CHANGED = trace -- <team>
+mod.PET_BATTLE_PET_TYPE_CHANGED = trace -- <team> <slot> <type>
+mod.PET_BATTLE_PET_ROUND_PLAYBACK_COMPLETE = trace -- <round number>
+mod.PET_BATTLE_PET_ROUND_RESULTS = trace -- <round number>
+mod.PET_BATTLE_PVP_DUEL_REQUESTED = trace
+mod.PET_BATTLE_PVP_DUEL_REQUEST_CANCEL = trace
+mod.PET_BATTLE_QUEUE_PROPOSAL_ACCEPTED = trace
+mod.PET_BATTLE_QUEUE_PROPOSAL_DECLINED = trace
+mod.PET_BATTLE_QUEUE_PROPOSE_MATCH = trace
+mod.PET_BATTLE_QUEUE_STATUS	 = trace
+mod.PET_BATTLE_TURN_STARTED = trace
+mod.PET_BATTLE_XP_CHANGED = trace -- <team> <slot> <exp>
+--]]
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/Raid.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,152 @@
+--- Turok Raid/Raid.lua
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+--- Defines the interfaces for raid tools
+local T = Turok
+local mod = T:NewModule('RaidReady')
+mod.OnInitialize = function(self)
+  self.db = TurokData.Lost
+  self.events = {
+    PARTY_MEMBERS_CHANGED = self.MembersChangedEvent,
+    PLAYER_SPECIALIZATION_CHANGED = self.SpecChangeEvent,
+    ENCOUNTER_START = self.EncounterStart,
+    UNIT_AURA = self.UnitAura
+  }
+end
+
+T.defaults.Lost = {
+  parent = 'UIParent',
+  anchor = 'BOTTOMRIGHT', anchorTo = 'BOTTOMRIGHT',
+  x = -300, y = 300,
+  height = 24*9, width = 72,
+  width = 72,
+  height = 24,
+  size = 11,
+  font = "Interface\\Addons\\Turok\\Media\\font\\ArchivoNarrow-Bold.ttf",
+
+  raidbuff = {
+    icon = {},
+    status = {},
+  },
+
+  durability = {
+
+  },
+
+  toast = {
+
+  },
+}
+local _G = _G
+local print = function(...) if Devian and DevianDB.workspace~= 1 then print('RaidInfo', ...) end end
+local CreateFrame, floor, GetRaidBuffTrayAuraInfo, NUM_LE_RAID_BUFF_TYPES = CreateFrame, math.floor, GetRaidBuffTrayAuraInfo, NUM_LE_RAID_BUFF_TYPES
+local GetSpecialization, GetSpecializationInfo, GetSpecializationInfoByID = GetSpecialization, GetSpecializationInfo, GetSpecializationInfoByID
+local IsInRaid, IsInGroup, GetInspectSpecialization = IsInRaid, IsInGroup, GetInspectSpecialization
+local find, match, sub = string.find, string.match, string.sub
+local GetRealmName, GetRaidRosterInfo, UnitGUID = GetRealmName, GetRaidRosterInfo, UnitGUID
+local db
+
+function mod:OnEnable()
+  db = self.db
+
+  self.raidbuffs = {} -- active raid buffs
+  self.buffinfo = {}   -- raid buff text
+  self.available = {}  -- availability info
+  self.units_raid = {}
+
+  self.raidbuffs_tray = _G.TurokRaidbuffsTray
+
+  -- seed raid buff analyzer assets
+  self.num_raidbuff_columns = floor(db.raidbuff.width / db.raidbuff.icon.width)
+  for i = 1, 9 do
+    --print('TurokRaidbuffButton'..i, self.raidbuffs_tray, 'TurokRaidbuffButton')
+    local buff = CreateFrame('Button', 'TurokRaidbuffButton'..i, self.raidbuffs_tray, 'TurokRaidbuffButton')
+
+    -- T.SetFrameLayout(buff, db.raidbuff)
+  end
+
+  db.raidevent = {}
+
+
+
+
+end
+
+function mod:PLAYER_SPECIALIZATION_CHANGED(e, unit)
+  local specID
+  --print(e, unit)
+  if unit == 'player' then
+    specID = GetSpecializationInfo(GetSpecialization())
+  else
+    --NotifyInspect(unit)
+    specID = GetInspectSpecialization()
+  end
+  if specID then
+    --print(GetSpecializationInfoByID(specID))
+  end
+end
+
+function mod:PARTY_MEMBERS_CHANGED(e, ...)
+  if IsInRaid() or IsInGroup() then
+    self.raidbuffs_frame:Show()
+    self:RaidBuffScan()
+  else
+  end
+end
+
+function mod:ENCOUNTER_START(e,...)
+  --print(e,...)
+end
+
+-- Updates available raid/party buffs
+function mod:RaidbuffsUpdate(unit)
+  if not (IsInGroup() or IsInRaid()) then
+    self.raidbuffs_tray:Hide()
+    return
+  end
+
+  local c = db.raidbuff
+  local k = 0
+  for i = 1, NUM_LE_RAID_BUFF_TYPES do
+
+    local rb = self.raidbuffs[i]
+    local buff = {GetRaidBuffTrayAuraInfo(i) }
+    --name, rank, texture, duration, expiration, spellId, slot
+    local isShown = false
+    if buff[1] then
+      isShown = true
+      self.raidbuffs[i] = buff
+    else
+      self.raidbuffs[i] = nil
+    end
+
+    if isShown then
+        rb:Show()
+        rb.bufftype:SetText(sub(_G['RAID_BUFF_'..i],0,2))
+        rb.spellname:SetText(self.buffinfo[i])
+
+        local pn = k                                     -- need (n-1) for lua grid math
+        local py = floor(pn / self.num_raidbuff_columns) * c.height
+        local px = (pn * c.width) % db.width      -- x-offset
+        --print('buff slot '..i..' (draw position '..k..')', pn, py, px)
+        rb:SetPoint(c.anchor, self.raidbuffs_tray, c.anchor, px, py)
+
+        k = k + 1
+      end
+  end
+  if k == 0 and self.raidbuffs_tray:IsVisible() then
+    self.raidbuffs_tray:Hide()
+  elseif not self.raidbuffs_tray:IsVisible() then
+    self.raidbuffs_tray:Show()
+  end
+end
+
+function mod:RosterScan()
+  local lim = 1
+  if IsInRaid() then
+    lim = 40
+  elseif IsInGroup() then
+    lim = 5
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/Raid.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,68 @@
+<Ui>
+
+  <Frame name="TurokRaidbuffsTray" alpha="0">
+    <Size x="200" y="600" />
+    <Anchors>
+      <Anchor point="RIGHT" x="45" y="-300" />
+    </Anchors>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture name="$parentBackground" parentKey="background" setAllPoints="true">
+          <Color r="1" g="1" b="1" a="1" />
+          <Gradient>
+            <ColorMin r="1" g="0" b="0.8" />
+            <ColoMax r="0.5" g="0" b="1" />
+          </Gradient>
+        </Texture>
+      </Layer>
+    </Layers>
+    <Frames>
+      <Button name="$parentAction" parentArray="_buttons" parentKey="action"  inherits="TurokButton">
+        <Script>
+          <OnShow>
+            if Turok.playerClass == 'HUNTER' then
+              if Turok.specPage == 1 then
+                self:SetText()
+              else
+                self:SetText("Lone Wolf")
+              end
+            end
+
+          </OnShow>
+        </Script>
+      </Button>
+    </Frames>
+  </Frame>
+
+  <Frame name="TurokRaidbuffButton" virtual="true" hidden="true">
+    <Anchors>
+      <Anchor point="CENTER" />
+    </Anchors>
+    <Size x="64" y="64" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture name="$parentBackground" parentKey="background" setAllPoints="true">
+          <Color r="0" g="0" b="0" a="1" />
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture name="$parentForeground" parentKey="foreground">
+          <Color r="0" g="1"  b="1" a="1" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="1" y="-1" />
+            <Anchor point="BOTTOMRIGHT" x="-1" y="1" />
+          </Anchors>
+        </Texture>
+        <Texture file="" name="BuffIcon" parentKey="icon">
+          <Anchor point="TOPLEFT" x="2" y="-2" />
+          <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentSpellName" parentKey="spellname" inherits="TurokFontDetail" />
+        <FontString name="$parentBuffType" parentKey="bufftype" inherits="TurokFontDetail" />
+        <FontString name="$parentCaster" parentKey="caster" inherits="TurokFontDetail" />
+      </Layer>
+    </Layers>
+  </Frame>
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/Toast.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,372 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 2/2/2016 12:09 AM
+
+local mod = Turok:NewModule("Toast", "AceTimer-3.0")
+local _G = _G
+local db
+local T, tostring, type, max, tinsert, UIParent, loadstring = _G.Turok, tostring, type, max, table.insert, _G.UIParent, loadstring
+--@debug@
+local DEBUG = true
+--@end-debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if not DEBUG then return end
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Toast', ...)
+  end
+end
+
+-- GLOBALS:BossBanner_OnEvent, BossBanner
+-- kill off the default BossBanner
+local old_bb = BossBanner_OnEvent
+BossBanner_OnEvent = function(self, event, ...)
+  T:Print(event, ...)
+end
+
+T.defaults.toast = {
+  alert_hold = 2,
+  alert_fade = 1,
+  alert_flash = .6,
+  anchor = 'TOP',
+  parent = 'UIParent',
+  anchorTo = 'TOP',
+  x = 0, y= -56,
+  loot = {
+    background_color = {},
+  }
+}
+
+mod.events = {
+  "ACHIEVEMENT_EARNED",
+  "CRITERIA_EARNED",
+  "LFG_COMPLETION_REWARD",
+  "GUILD_CHALLENGE_COMPLETED",
+  "CHALLENGE_MODE_COMPLETED",
+  "LOOT_ITEM_ROLL_WON",
+  "SHOW_LOOT_TOAST",
+  "SHOW_LOOT_TOAST_UPGRADE",
+  "SHOW_PVP_FACTION_LOOT_TOAST",
+  "PET_BATTLE_CLOSE",
+  "STORE_PRODUCT_DELIVERED",
+  "GARRISON_BUILDING_ACTIVATABLE",
+  "GARRISON_MISSION_FINISHED",
+  "GARRISON_FOLLOWER_ADDED",
+  "GARRISON_RANDOM_MISSION_ADDED",
+  "BOSS_KILL",
+  "ENCOUNTER_LOOT_RECEIVED",
+}
+
+mod.events_args = {
+  ['ACHIEVEMENT_EARNED'] = {12, false},
+  ['ACHIEVEMENT_EARNED'] = {12, true},
+  ['LFG_COMPLETED_REWARD'] = {},
+}
+local y_factor = -1
+local x_factor = 0
+local tkAlerts = TkAlertContainer
+local AlertFrame_FixPosition = function(alertFrame)
+  print('   pos:', alertFrame.order, cKey(alertFrame.x), cWord(alertFrame.y))
+  alertFrame:SetPoint('TOPLEFT', tkAlerts, 'TOPLEFT', alertFrame.x, alertFrame.y)
+end
+
+--- Goes through the alert container index and fixes the frame positions
+-- used before and after an alert frame changes visibility
+local AlertContainer_Update = function(self)
+  local anum = 1
+  local offset = 0
+  for i, alert in ipairs(tkAlerts.alerts) do
+    if alert:IsShown() then
+      alert.x = offset * x_factor
+      alert.y = offset * y_factor
+      alert.drawHeight = alert.ename:GetStringHeight()+ alert.desc:GetStringHeight()
+      alert.order = anum
+      anum = anum + 1
+      offset = offset + alert.drawHeight
+      print('   draw height:', i, cText(alert.drawHeight))
+      print('      position:', i, cPink(offset))
+      AlertFrame_FixPosition(alert)
+      print('index', i, 'shifted from position', cNum(alert.order), 'to', cNum(anum), ' at draw point', alert.y)
+      print('    draw distance update ', cNum(0), 'x', cNum(offset))
+    else
+      print('index', i, 'is not visible')
+    end
+  end
+  self:SetHeight(offset)
+end
+
+--- config mode blocking command
+local AlertFrame_Block = function(alertFrame)
+  -- only do stuff when configMode is on
+  if not mod.configMode then
+    return
+  end
+end
+
+--- if not config mode, then fire at the end of fadeIn animation to queue fadeOut
+local AlertFrame_Pin = function(alertFrame)
+  if not mod.configMode then
+    alertFrame.fadeOut.a1:SetStartDelay(db.alert_hold)
+    alertFrame.fadeOut:Play()
+  end
+end
+
+--- if not config mode, then fire at the end of fadeOut to remove the frame from view and trigger positions update
+local AlertFrame_Remove = function(alertFrame)
+  mod.num_events = mod.num_events - 1
+  alertFrame:Hide()
+  print(mod.num_events)
+  AlertContainer_Update(tkAlerts)
+end
+
+--- orders all visible frames to fadeOut
+-- @param stagger forces the spacing of startDelay times for each frame
+local AlertContainer_Clear = function(self, stagger)
+  stagger = stagger or 0.1
+  for i = #self.alerts, 1, -1 do
+    print('clear check', i)
+    local alert = self.alerts[i]
+    if alert:IsShown() and not alert.fadeOut:IsPlaying() then
+      alert.fadeOut.a1:SetStartDelay((#self.alerts-i)* stagger)
+      alert.fadeOut:Play()
+    end
+  end
+end
+local AlertContainer_Unlock = function()
+end
+
+--- Displays a new alert
+-- @param name text naming the class of event that occurred
+-- @param text alert subtext describing basic info about the event
+-- @order order (optional) sets display slot of the alert
+local function AlertContainer_ShowAlert(self, name, text, order)
+  local db = TurokData.toast
+  mod.num_events = mod.num_events + 1
+
+  local alertFrame
+  if not order then
+  local i = 1
+    while i <= #self.alerts and not alertFrame do
+      if not self.alerts[i]:IsShown() then
+        alertFrame = self.alerts[i]
+        print('re-using alert frame #', i)
+      end
+      i = i +1
+    end
+  else
+    alertFrame = self.alerts[order]
+  end
+
+  if not alertFrame then
+    alertFrame = CreateFrame('Frame', 'TkAlertPanel'..(order or #self.alerts+1), self, 'TkAlertFrame')
+    self.alerts[#self.alerts+1] = alertFrame
+    print('creating new alert frame', #self.alerts)
+
+    alertFrame.Pin = AlertFrame_Pin
+    alertFrame.Remove = AlertFrame_Remove
+  end
+
+  alertFrame.ename:SetText(name)
+  local height1 = alertFrame.ename:GetStringHeight()
+  alertFrame.desc:SetText(text)
+  local height2 = height1+ alertFrame.desc:GetStringHeight()
+  alertFrame.desc:SetPoint('TOPLEFT', alertFrame, 'TOPLEFT', 0, -height1)
+
+  alertFrame.order = order or mod.num_events
+  alertFrame:SetSize(300, height2)
+  alertFrame:Show()
+  --alertFrame.flashIn.a1:SetDuration(db.alert_flash/2)
+  --alertFrame.flashIn.a2:SetDuration(db.alert_flash/2)
+  alertFrame.flashIn:Play()
+  if not mod.configMode then
+    AlertContainer_Update(tkAlerts)
+  end
+end
+
+
+--- updates the completed missions index and returns info on the mission ID if passed
+local completedMissions
+local Garrison_UpdateCompleteMissions = function(missionID)
+  completedMissions = C_Garrison.GetCompleteMissions()
+  --- slide entries around for reference
+  for i, set in ipairs(completedMissions) do
+    if i ~= set.missionID then
+      completedMissions[set.missionID] = set
+      completedMissions[i] = nil
+    end
+  end
+
+  if missionID and completedMissions[missionID] then
+    local m = completedMissions[missionID]
+    return m.name, m.location, m.locPrefix, m.isRare, m.followers, m.rewards, m.state
+  else
+    return false
+  end
+end
+--- container events handler
+local AlertContainer_OnEvent = function (self, event, ...)
+  print(event, ...)
+  tkAlerts:Show()
+  if event == 'SHOW_LOOT_TOAST' then
+    local typeIdentifier, itemLink, quantity, specID, sex, isPersonal, lootSource = ...;
+    if typeIdentifier == "currency" then
+      AlertContainer_ShowAlert(self, itemLink, 'x'..quantity)
+    elseif typeIdentifier == "item" then
+      local _, _, _, ilvl, _, _, _, _, equipSlot = GetItemInfo(itemLink)
+      AlertContainer_ShowAlert(self, itemLink, tostring(ilvl)..' '..tostring(_G[equipSlot]))
+    end
+  elseif event == 'GARRISON_MISSION_FINISHED' then
+    local missionID = ...
+    local name, location, locPrefix, isRare, followers, rewards = Garrison_UpdateCompleteMissions(missionID)
+    local mission_info = {
+      (isRare and ('|cFF44BBFF') or ('|cFFFFFF00')..name.. '|r'),
+    }
+
+    if followers then
+      for i, guid in ipairs(followers) do
+        print(C_Garrison.GetFollowerInfo(guid))
+      end
+    end
+
+      --'|T:'..icon..':0
+
+
+    AlertContainer_ShowAlert(self, 'Mission Complete')
+  elseif event == 'GARRISON_BUILDING_ACTIVATABLE' then
+    local missionID = ...
+  elseif event == 'ACHIEVEMENT_EARNED' then
+  elseif event == 'LFG_COMPLETION_REWARD' then
+    local name, typeID, subtypeID, textureFilename, moneyBase, moneyVar, experienceBase, experienceVar, numStrangers, numRewards = GetLFGCompletionReward()
+    local _, _, _, _, hasBonusStep, isBonusStepComplete = C_Scenario.GetInfo();
+
+  end
+end
+
+local AlertContainer_Test = function()
+  if not mod.configMode then
+    print('starting test mode')
+    tkAlerts:Show()
+    mod.configMode = true
+
+    tkAlerts.configBG:Show()
+    tkAlerts.configBG:SetTexture(0,0.5,0,0.5)
+    tkAlerts:RegisterForDrag('LeftButton')
+    tkAlerts:EnableMouse(true)
+
+    for i, frame in ipairs(tkAlerts.tools) do
+      frame:Show()
+    end
+    -- test fillers
+    local _,_, offset, range = GetSpellTabInfo(2)
+    print(offset, range)
+
+    for i, event in ipairs(mod.events) do
+      --print(i, event)
+      local _, id = GetSpellBookItemInfo(math.random(offset, offset+range), 'spell')
+      print(id)
+
+      local name = GetSpellLink(id)
+      local text = GetSpellDescription(id)
+      if not tkAlerts.alerts[i] then
+        print('creating alert frame #', i)
+        AlertContainer_ShowAlert(tkAlerts, name, text, i)
+      else
+        print('updating alert frame #', i)
+        local alert = tkAlerts.alerts[i]
+        alert:Show()
+        alert.order = i
+        alert.ename:SetText(name)
+        alert.desc:SetText(text)
+        if alert.fadeOut:IsPlaying() then
+          alert.fadeOut:Stop()
+          alert.backdrop:SetAlpha(0.5)
+        end
+
+        alert.flashIn:Play()
+      end
+    end
+    AlertContainer_Update(tkAlerts)
+  else
+    tkAlerts:EnableMouse(false)
+    tkAlerts.configBG:Hide()
+    mod.configMode = nil
+    for i, frame in ipairs(tkAlerts.tools) do
+      frame:Hide()
+    end
+    for i, alert in ipairs(tkAlerts.alerts) do
+      for j, frame in ipairs(alert.tools) do
+        frame:Hide()
+      end
+      alert.fadeOut.a1:SetStartDelay(i*0.2)
+      alert.fadeOut:Play()
+    end
+
+  end
+end
+
+mod.OnEnable = function()
+  db = TurokData.toast
+
+  --- find the closest frame to the center bottom and anchor to that
+
+  local cX, cY = (GetScreenWidth() / 2), (GetScreenHeight() / 2)
+  local min_skewness = cX
+  local max_centrality, center_frame
+  for n, f in ipairs({UIParent:GetChildren()}) do
+    if type(f) == 'table' and f.GetObjectType and f.GetName then
+      if f.IsForbidden and f:IsForbidden() then
+        print(n, 'is a forbidden object')
+      else
+        local name = f:GetName()
+
+        if f:IsVisible() and f:IsMouseEnabled() then
+          print(name and name or tostring(f):sub(8), 'is a frame!')
+          local x = f:GetCenter()
+          local y = f:GetTop()    -- Y need center top position
+          if (x and y) and (y <= cY) then
+            x = math.abs(x - cX) -- X works in either direction
+
+            -- distance of current - distant of record / max to get ratio of 1 where direct center results in 1
+            local skewness_factor = (cX - x) / cX
+            local vertness_factor = y / cY
+            local centrality = skewness_factor * vertness_factor
+            print('result: (', floor(x), floor(y), ') = ', skewness_factor, 'skew,', vertness_factor, 'vertness.\nTotal score:', cNum(centrality))
+            if (not max_centrality) or max_centrality < centrality then
+              center_frame = f
+              max_centrality = centrality
+              print(cWord(name and name or tostring(f):sub(8)),cPink(' is the new record!'))
+            end
+
+          end
+        end
+      end
+    end
+  end
+
+  mod.num_events = 0
+  tkAlerts.alerts = {}
+  for i, event in ipairs(mod.events) do
+    tkAlerts:RegisterEvent(event)
+  end
+
+  tkAlerts.Clear = AlertContainer_Clear
+  tkAlerts.Unlock = AlertContainer_Unlock
+  tkAlerts.Close = AlertContainer_Test
+
+  tkAlerts:ClearAllPoints()
+  tkAlerts:SetPoint(db.anchor, db.parent, db.anchorTo, db.x, db.y)
+  tkAlerts.x = db.x
+  tkAlerts.y = db.y
+  tkAlerts.parent = db.parent
+  tkAlerts.anchor = db.anchor
+  tkAlerts.anchorTo = db.anchorTo
+  tkAlerts:EnableMouse(false)
+
+  tkAlerts:SetScript('OnEvent', AlertContainer_OnEvent)
+  T:RegisterChatCommand("alert", AlertContainer_Test)
+  T:RegisterChatCommand("atest", function()
+    AlertContainer_OnEvent(tkAlerts, 'GARRISON_MISSION_FINISHED', 327)
+  end)
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Modules/Utilities/Utilities.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,252 @@
+<Ui>
+  <Frame name="TkAlertContainer" parent="UIParent" movable="true">
+    <Scripts>
+      <OnDragStart>
+        self.cx = self:GetLeft()
+        self.cy = self:GetTop()
+        self:StartMoving()
+      </OnDragStart>
+      <OnDragStop>
+        self:StopMovingOrSizing()
+        self.x = self.x + self:GetLeft() -  self.cx
+        self.y = self.y + self:GetTop() - self.cy
+        self:ClearAllPoints()
+        self:SetPoint(self.anchor, self.parent, self.anchorTo, self.x, self.y)
+      </OnDragStop>
+    </Scripts>
+    <Anchors>
+      <Anchor point="LEFT" x="20" y="0" />
+    </Anchors>
+    <Size x="300" y="100" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture setAllPoints="true" parentKey="configBG" hidden="true">
+          <Color r="1" b="1" g="1" a="0.5" />
+        </Texture>
+        <Texture setAllPoints="true" parentKey="flashBG" alpha="0">
+        </Texture>
+      </Layer>
+    </Layers>
+    <Animations>
+      <AnimationGroup parentKey="flashIn" setToFinalAlpha="true">
+        <Alpha change="1" childKey="flashBG" parentKey="a1" duration=".3" order="1" />
+        <Alpha change="-1" childKey="flashBG" parentKey="a2" duration=".6" order="2" />
+        <Scripts>
+          <OnPlay>
+            self:GetParent().flashBG:Show()
+          </OnPlay>
+          <OnFinished>
+            self:GetParent().flashBG:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+    </Animations>
+    <Frames>
+      <Button parentKey="clear" name="$parentButton_C" inherits="TurokButton" hidden="true">
+        <Size x="24" y="24" />
+        <Anchors>
+          <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" x="-4" y="0" />
+        </Anchors>
+        <Scripts>
+          <OnClick>
+            self:GetParent():Clear(self:GetParent())
+          </OnClick>
+        </Scripts>
+      </Button>
+      <Button parentArray="tools" parentKey="close" name="$parentButton_Close" inherits="TurokButton" hidden="true">
+        <Size x="64" y="24" />
+        <Anchors>
+          <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" x="0" y="-4" />
+        </Anchors>
+        <Scripts>
+          <OnClick>
+            self:GetParent():Close()
+          </OnClick>
+        </Scripts>
+      </Button>
+      <Button parentArray="tools" name="$parentButton_Unlock" inherits="TurokButton" hidden="true">
+        <Size>
+          <AbsDimension y="24" />
+            </Size>
+        <Scripts>
+          <OnClick>
+            self:GetParent():Unlock()
+          </OnClick>
+        </Scripts>
+        <Anchors>
+          <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeKey="$parent.close" x="2" y="0" />
+          <Anchor point="TOPRIGHT" relativePoint="BOTTOMRIGHT" x="0" y="-2" />
+        </Anchors>
+      </Button>
+    </Frames>
+  </Frame>
+
+  <Frame name="TkAlertFrame" hidden="true" virtual="true">
+
+    <Animations>
+      <AnimationGroup name="flashIn" parentKey="flashIn" looping="NONE" ignoreFramerateThrottle="true" setToFinalAlpha="true">
+        <Alpha name="a1" parentKey="a1" childKey="flash" duration=".1" fromAlpha="0" toAlpha="1" order="1" />
+        <Alpha name="a2" parentKey="a2" childKey="flash" duration=".5" fromAlpha="1" toAlpha="0" order="2" />
+        <Alpha name="a3" parentKey="a3" childKey="backdrop" duration=".1" fromAlpha="0" toAlpha="1" order="1" />
+        <Scripts>
+          <OnPlay>
+            print(self:GetParent():GetParent():GetName())
+            print(self:GetParent():GetParent().num_events)
+          </OnPlay>
+          <OnFinished>
+            self:GetParent():Pin()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup name="fadeOut" parentKey="fadeOut" looping="NONE" ignoreFramerateThrottle="true">
+        <Alpha startDelay="5" name="a1" parentKey="a1" duration="1.5" change="-1" order="1" />
+        <Scripts>
+          <OnFinished>
+
+            self:GetParent():Remove()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+      <AnimationGroup name="sweepOver" parentKey="sweepOver" looping="NONE" ignoreFrameRateThrottle="true">
+        <Alpha
+            name="a1" parentKey="a1" childKey="flare" change="1" duration="0.15" order="1" />
+        <Translation
+            name="t1" parentKey="t1" childKey="flare" offsetX="200" offsetY="0" startDelay="0.2" duration=".4" order="1" />
+        <Rotation
+            name="r1" parentKey="r1" childKey="flare" degrees="180" duration="0.55" order="1" />
+        <Alpha
+            name="a2" parentKey="a2" childKey="flare" change="-1" duration="0.15" order="2" startDelay=".8" />
+        <Scripts>
+          <OnPlay>
+            self:GetParent():GetParent().flare:Show()
+          </OnPlay>
+          <OnFinished>
+            self:GetParent():GetParent().flare:Hide()
+          </OnFinished>
+        </Scripts>
+      </AnimationGroup>
+    </Animations>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture setAllPoints="true" parentKey="backdrop" alpha="1" alphaMode="BLEND">
+          <Color r="0" g="0" b="0" a="0.5" />
+        </Texture>
+        <Texture setAllPoints="true" parentKey="flash" alpha="0" alphaMode="ADD">
+          <Color r="1" b="1" g="1" a="1" />
+          <Gradient orientation="HORIZONTAL">
+            <MinColor r="1" g="1" b="1" a="1" />
+            <MaxColor r="1" g="1" b="1" a="0" />
+          </Gradient>
+        </Texture>
+        <Texture parentKey="flare" file="Interface\Cooldown\star4" alphaMode="ADD">
+          <Color r="0.3" g="0.6" b="1" a="0.8"/>
+          <Size x="24" y="24" />
+          <Anchors>
+            <Anchor point="LEFT" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString inherits="TurokFont" parentKey="ename" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+          </Anchors>
+        </FontString>
+        <FontString inherits="TurokFontDetail" parentKey="desc" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeKey="ename" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+    <Frames>
+
+      <Button parentArray="tools" name="$parentButton_Block" inherits="UIPanelSquareButton" hidden="true" >
+        <Scripts>
+          <OnClick>
+            self:GetParent():Block(self)
+          </OnClick>
+        </Scripts>
+        <Size x="30" y="30" />
+        <Anchors>
+          <Anchor point="TOPRIGHT" x="36" y="3" />
+        </Anchors>
+      </Button>
+    </Frames>
+  </Frame>
+
+  <Frame virtual="true" hidden="true" name="TkPetSwatchTemplate" enableMouse="true">
+    <Layers>
+      <Layer level="OVERLAY">
+        <FontString inherits="TurokFont" name="PetName" parentKey="name" />
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame virtual="true" hidden="true" name="TkPetSpellTemplate" enableMouse="true">
+    <Scripts>
+      <OnEnter>
+        print(self:GetName(), self.spellID, 'enter')
+        GameTooltip:SetOwner(self, 'ANCHOR_TOP')
+        --GameTooltip:SetPetAction(self.spellID)
+        GameTooltip:Show()
+      </OnEnter>
+      <OnLeave>
+        print(self:GetName(), 'leave')
+        GameTooltip:Hide()
+      </OnLeave>
+    </Scripts>
+    <Size x="64" y="64" />
+    <Layers>
+      <Layer level="ARTWORK">
+        <Texture name="Icon" parentKey="icon" setAllPoints="true">
+          <Anchors>
+            <Anchor point="CENTER" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <Texture name="StrongWeak" parentKey="strongWeakHint">
+          <Size x="32" y="32" />
+          <Anchor>
+            <Anchor point="CENTER" />
+          </Anchor>
+        </Texture>
+        <FontString inherits="TurokFont" name="TurnsLeft" parentKey="turnsLeft" setAllPoints="true" />
+      </Layer>
+      <Layer level="HIGHLIGHT">
+        <FontString inherits="TurokFontDetail" name="Description" parentKey="spellDesc" setAllPoints="true" />
+        <FontString inherits="TurokFontDetail" name="Damage" parentKey="damageModifier" setAllPoints="true" />
+        <Texture alphaMode="ADD" setAllPoints="true">
+          <Color a="1" r="1" g="1" b="0" />
+        </Texture>
+      </Layer>
+    </Layers>
+    <Animations>
+      <AnimationGroup name="popIn" parentKey="popIn" ignoreFrameRateThrottle="true" looping="NONE" setToFinalAlpha="true">
+        <Scripts>
+          <OnPlay>
+            self:GetParent():Show()
+            self:GetParent():SetAlpha(0)
+          </OnPlay>
+        </Scripts>
+        <Alpha childKey="spellIcon" duration="0.4" change="1" order="1" />
+        <Scale childKey="spellIcon" duration="0.4" fromScaleX="0.1" fromScaleY="0.1" toScaleX="1" toScaleY="1" order="1" />
+      </AnimationGroup>
+      <AnimationGroup name="popOut" parentKey="popOut" ignoreFramerateThrottle="true" looping="NONE" setToFinalAlpha="true">
+        <Scripts>
+          <OnFinished>
+            self:GetParent():Hide()
+          </OnFinished>
+        </Scripts>
+        <Alpha childKey="spellIcon" duration="0.4" change="-1" order="1" />
+        <Scale childKey="spellIcon" duration="0.4" fromScaleX="1" toScaleX=".1" fromScaleY="1" toScaleY=".1" order="1" />
+      </AnimationGroup>
+    </Animations>
+  </Frame>
+
+  <Include file="Raid.xml" />
+  <Script file="Toast.lua" />
+  <Script file="Raid.lua" />
+  <Script file="PetBattle.lua" />
+</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Turok.iml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="LUA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Turok.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,554 @@
+--- Turok
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+--- Defines the mechanisms for the storage and dispatch of events data.
+--@debug@
+--@end-debug@
+-- GLOBALS: LibStub, Turok, TurokData, ReloadUI
+local ADDON, Tk = ...
+local db, L
+local MAJOR, MINOR = "Turok", "@project-revision@"
+local T, LGIST, _G = Tk.Addon, Tk.LGIST, _G
+
+
+local pcall, type, ipairs, pairs, format, tinsert, match, strpad, error = pcall, type, ipairs, pairs, string.format, table.insert, string.match, string.rep, error
+local PlaySoundFile, LoadAddOn, IsAddOnLoaded, UnitName, UnitGUID, UnitPowerMax = PlaySoundFile, LoadAddOn, IsAddOnLoaded, UnitName, UnitGUID, UnitPowerMax
+local GetSpecializationInfo, GetSpecialization, UnitSpellHaste, GetActiveSpecGroup = GetSpecializationInfo, GetSpecialization, UnitSpellHaste, GetActiveSpecGroup
+local UnitInfo, UnitCastingInfo, UnitChannelInfo = UnitInfo, UnitCastingInfo, UnitChannelInfo
+local rawset, unpack, tostring, setmetatable, xpcall, unpack = rawset, unpack, tostring, setmetatable, xpcall, unpack
+
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Turok', ...)
+  end
+end
+local GetPrint = function(trace)
+  return trace and print or function() end
+end
+--@end-debug@
+
+
+
+--- Pull saved variables and make them sane
+function T.OnInitialize(T)
+
+  --@debug@
+  local tmpidx
+  if TurokData and TurokData.spirit and TurokData.spirit.timerindex then
+    tmpidx = TurokData.spirit.timerindex
+  end
+  TurokData = Turok.defaults
+  if tmpidx then
+    TurokData.spirit.timerindex = tmpidx
+  end
+  --@end-debug@
+  T.db = _G.TurokData
+  LibStub("LibFog-1.0"):Embed(T)
+  if T.db.queue_for_wipe then
+    T:Print("db reset flag received")
+    local index = _G.TurokData.spirit.timerindex
+    _G.TurokData = T.defaults
+    T.db = _G.TurokData
+    T.db.spirit.timerindex = index
+  end
+
+  T:RegisterChatCommand("tkr", function()
+    T.db.queue_for_wipe = true
+    ReloadUI()
+  end)
+
+  -- db hierarchy
+  for k, v in pairs(T.db) do
+    if type(v) == 'table' then
+      --@debug@
+      --print('|cFF44FF88loading stored db|r: db.|cFF44AAFF'.. k ..'|r')--@end-debug@
+      T.LinkTable(T.db, v, 'db', k)
+    end
+  end
+  setmetatable(T.db,
+    {__newindex = function (t, k, v)
+      rawset(t,k,v)
+      if type(v) == 'table' then
+        --@debug@
+        --print('|cFF44FF88creating new db|r: db.|cFF44AAFF'.. k ..'|r')--@end-debug@
+        T.LinkTable(T.db, v, 'db', k)
+      end
+    end})
+  T.L = setmetatable({}, {__call = function(t, s) return t[s] or s end})
+end
+
+--- Get everything rolling
+function T:OnEnable()
+  db = TurokData
+  L = T.L
+
+  self.dispatchQueue = {}
+  self.sharedTables = {}
+  local skip = {modules = true, db = true, defaultModuleLibraries = true, orderedModules = true, prototype = true, events = true} -- local data that exists before init
+  for id, mod in pairs(self.orderedModules) do
+    mod.ID = id
+    mod.RegisterCallback = self.RegisterCallback
+    print('load', mod:GetName())
+    for k, v in pairs(mod) do
+      if not skip[k] then
+        if match(k, '^[%u_]+$') then
+          if not self.dispatchQueue[k] then
+            self.dispatchQueue[k] = {}
+          end
+          self:RegisterEvent(k, 'Dispatch')
+          self.dispatchQueue[k][id] = mod:GetName()
+        end
+      end
+    end
+  end
+
+  print('events')
+  for k, v in pairs(self.dispatchQueue) do
+    print('  ',k,'->', unpack(v))
+  end
+
+  print('shared')
+  for k, v in pairs(self.sharedTables) do
+    print('  ',k)
+  end
+  --@debug@
+  self:Print(MAJOR, MINOR, 'enabled.')--@end-debug@
+  self:RegisterChatCommand("tkl", function()
+    PlaySoundFile([[Interface\Addons\Turok\Media\sound\wilhelm.ogg]])
+    if not IsAddOnLoaded("Turok_Config") then
+      local loaded, message = LoadAddOn("Turok_Config")
+      if not loaded then
+        return T:Print("|cFFFF0000Load-on-Demand failed!|r Reason: " .. message)
+      end
+      --self.config = LibStub('AceConfigDialog-3.0'):AddToBlizOptions(MAJOR)
+    end
+    InterfaceOptionsFrame_OpenToCategory(MAJOR)
+    InterfaceOptionsFrame_OpenToCategory(MAJOR)
+  end)
+
+  self:RegisterChatCommand("unlock", function()
+    T.unlocked = (not T.unlocked) and true or nil
+    if T.unlocked then
+      self:Print('frames unlocked')
+    else
+      self:Print('frames locked')
+    end
+    for k, v in pairs(self.orderedModules) do
+      if v.UpdateLocked then
+      v:UpdateLocked()
+        end
+    end
+  end)
+
+  self:RegisterChatCommand("lsm", function(input)
+    local a1, n = self:GetArgs(input, 1,0)
+    local a2 = self:GetArgs(input, 1, n)
+    self:Print(self.LSM:Fetch(a1, a2))
+  end)
+
+  local name, realm = UnitFullName('player')
+  LGIST.RegisterCallback(self, "GroupInSpecT_InspectReady", 'GIST_InspectReady')
+  LGIST.RegisterCallback(self, "GroupInSpecT_Update", 'GIST_Update')
+  self:RegisterCharacterInfo(name, realm)
+
+  if InCombatLockdown() then
+    T.inCombat = true
+  end
+
+  T:PLAYER_SPECIALIZATION_CHANGED('PLAYER_SPECIALIZATION_CHANGED','player')
+  for order, event in ipairs(self.events) do
+    self:RegisterEvent(event, 'Dispatch')
+  end
+end
+
+do
+  local bucket_events = {['PLAYER_SPECIALIZATION_CHANGED'] = {}, ['PLAYER_TALENT_UPDATE'] = {}}
+  local bucket_start = {}
+  local event_queue = {}
+  local wipe, debugstack = table.wipe, debugstack
+
+  --- Passes event data to the appropriate handlers, and runs any callback returned
+  -- @param e event name
+  -- @param ... event arguments as given by WoW Lua
+  -- @return callback1 runs after its corresponding method has been dispatched to
+  -- @return callback2 runs after all dispatching has completed, in the order that dispatches occurred
+  function T:Dispatch(event, ...)
+    if not (self[event] or self.dispatchQueue[event]) then
+      return
+    end
+
+
+    if bucket_events[event] then
+      local unit = ...
+      if unit and unit ~= 'player' then
+        return
+      end
+
+      print(cText('*** Bucket Event:'), cWord(event))
+      if not bucket_start[event] then
+        self['TKBATCH_'..event] = {}
+        bucket_start[event] = GetTime()
+        T:ScheduleTimer( function()
+          print('*** Firing bucketed event ('..#bucket_events[event]..'):', event, unpack(bucket_events[event][#bucket_events[event]]))
+          T:Dispatch('TKBATCH_'..event, unpack(bucket_events[event][#bucket_events[event]]))
+          bucket_start[event] = nil
+
+          self['TKBATCH_'..event] = nil
+        end,0.1)
+      end
+      tinsert(bucket_events[event], {...})
+      return
+    else
+      event = event:gsub('^TKBATCH_', '')
+    end
+
+
+    if event ~= 'COMBAT_LOG_EVENT_UNFILTERED' then print('|cFFFF0088received|r', event, ...) end
+    local args = {event, ...}
+
+    -- if addon is listening directly
+    if self[event] then
+      print(cWord(event))
+      local reg = function() print(unpack(args)) self[event](self, unpack(args)) end
+      tinsert(event_queue, {self, event, reg})
+
+
+    -- if modules want this event directly
+    end
+    if self.dispatchQueue[event] then
+      for id, name in pairs(self.dispatchQueue[event]) do
+        local mod = self.orderedModules[id]
+        local reg = function() print(unpack(args)) mod[event](mod, unpack(args)) end
+        tinsert(event_queue, {mod, event, reg})
+      end
+    else
+      print("Received", event, "but nothing is listening to it.")
+    end
+
+    for i, entry in ipairs(event_queue) do
+      local handler, event, func = unpack(entry)
+      print('->', cWord(handler:GetName()), cBool(result), cText(message))
+      local result, message = xpcall(func, function(m) print(m, debugstack()) return debugstack() end)
+
+      if bucket_events[event] then
+        wipe(bucket_events[event])
+        bucket_events[event].history = {}
+      end
+    end
+    wipe(event_queue)
+  end
+end
+--- Store character data to speed up spec-specific load time
+function T:RegisterCharacterInfo(name,realm)
+  local classLocalized, classEnglish, classID = UnitClass('player')
+
+  -- set savedvars
+  self.playerName = name
+  self.playerRealm = realm
+  self.playerClass = classEnglish
+  self.playerClassLocalized = classLocalized
+  self.playerClassID = classID
+  self.GUID = UnitGUID('player')
+  local namerealm = name..'-'..realm
+  if not db.char[namerealm] then
+    db.char[namerealm]  = {
+      spellBook = {}
+    }
+  end
+  T.spellBook = db.char[namerealm].spellBook
+
+  -- push combat ratings calc
+  self:COMBAT_RATING_UPDATE()
+end
+
+--- Polls for specialization info until it's available
+function T:RegisterSpecEvents()
+  local specPage = GetSpecialization()
+  if specPage then
+
+    local specID, specName, specDesc, specTexture = GetSpecializationInfo(specPage)
+    local specGroup = GetActiveSpecGroup()
+    print('Turok', cText('* Spec Info:'), GetSpecializationInfo(GetSpecialization()))
+    if T.specID and specID ~= T.specID then
+      print('pushing out old spec data')
+      T.previousSpec = {
+        specPage = T.specPage,
+        specGroup = T.specGroup,
+        specID = T.specID,
+        specName = T.specName,
+        specTexture = T.specTexture
+      }
+    elseif not T.previousSpec then
+      print('first run probably')
+      T.previousSpec = {}
+    end
+
+
+    T.specUpdate = (specID ~= T.specID)
+    T.talentsChanged = (T.specGroup ~= specGroup)
+    T.specPage = specPage
+    T.specGroup = specGroup
+    T.specID = specID
+    T.specName = specName
+    T.specTexture = specTexture
+  else
+    print('Turok', cText('* No Spec Info yet, start polling'))
+    -- repeat until we get something to update with
+    T:ScheduleTimer('RegisterSpecEvents', 1)
+  end
+
+  if T.specID and not T.__specevents then
+    T.__specevents = true
+    T:PLAYER_SPECIALIZATION_CHANGED('PLAYER_SPECIALIZATION_CHANGED','player')
+    T:RegisterEvent('PLAYER_TALENT_UPDATE', 'Dispatch')
+    T:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', 'Dispatch')
+    T:RegisterEvent('ACTIVE_TALENT_GROUP_CHANGED', 'Dispatch')
+    T:RegisterEvent('UNIT_SPELLCAST_INTERRUPTED', 'Dispatch')
+  end
+end
+
+--- Event handler wrapper for the Specialization getter used at load time
+  local GetSpecializationSpells, GetSpellInfo = GetSpecializationSpells, GetSpellInfo
+  function T:PLAYER_SPECIALIZATION_CHANGED(event, unit)
+    -- make sure this was fired for player
+    if (unit ~= 'player') then
+      return
+    end
+
+
+    print('Turok', '|cFF00FF00'.. event)
+    -- Set current spec values and fill out previousSpec table
+    T:RegisterSpecEvents()
+    local ot = T.previousSpec
+
+    -- Is the new spec different?
+    if T.specUpdate then
+      print(cText('Spec changed from'), cWord(ot.specName), cText('to'), cKey(T.specName))
+
+      if ot.specPage and not T.spellBook[ot.specPage] then
+        T.spellBook[ot.specPage] = {}
+      end
+
+      local spellBook = {GetSpecializationSpells(T.specPage)}
+      if not T.spellBook[T.specPage] then
+        T.spellBook[T.specPage] = {}
+        for i = 1, #spellBook, 2 do
+          T.spellBook[T.specPage][spellBook[i]] = GetSpellInfo(spellBook[i])
+          if (not ot.specPage) or (T.spellBook[ot.specPage] and not T.spellBook[ot.specPage][spellBook[i]]) then
+            print(cText('activating for spell'), T.spellBook[T.specPage][spellBook[i]])
+          end
+        end
+      end
+
+
+
+      -- list changed spells for use by spell posession checks
+      local diff = {
+        gained = {},
+        lost = {}
+      }
+
+      --- Check against current for removed spells
+      if T.spellBook[ot.specPage] then
+        for id, spellInfo in pairs(T.spellBook[ot.specPage]) do
+          if not T.spellBook[ot.specPage] then
+            tinsert(diff.lost, spellInfo)
+            print('lost', spellInfo.spellName)
+          else
+            print('keep', spellInfo.spellName)
+          end
+        end
+      end
+
+      T.spellBook.change = diff
+    end
+  end
+
+  local GetInventoryItemID, GetItemSpell, GetSpellInfo, GetItemInfo = GetInventoryItemID, GetItemSpell, GetSpellInfo, GetItemInfo
+  function T:PLAYER_EQUIPMENT_CHANGED(event, slot, hasItem)
+    print('Equip slot #', slot, ' has?', hasItem)
+    if hasItem then
+      local itemID = GetInventoryItemID('player', slot)
+      print('itemID?', itemID)
+      local spellName = GetItemSpell(itemID)
+      print('spell?', spellName)
+      local _, spellID
+      if spellName then
+        _, _, _, _, _, _, spellID = GetSpellInfo(spellName)
+      end
+
+      local name, link, quality, iLevel, reqLevel, class, subclass, maxStack, equipSlot, texture, vendorPrice = GetItemInfo(itemID)
+      T.equipped[slot] = {
+        itemID = itemID,
+        spellName = spellName,
+        spellID = spellID,
+        name = name,
+        link = link,
+        equipSlot = equipSlot,
+        texture = texture,
+      }
+      print('equipped', name)
+
+    else
+      self.equipped[slot] = nil
+    end
+  end
+
+  local GetPowerRegen, UnitPowerType = GetPowerRegen, UnitPowerType
+  function T:COMBAT_RATING_UPDATE(t,e)
+    --@debug@
+    print('|cFF00FFFFCOMBAT_RATING_UPDATE')--@end-debug@
+    self.haste = UnitSpellHaste('player')
+    self.powerRegen = GetPowerRegen()
+    self.powerType = UnitPowerType('player')
+    self.castTimeMod = 1+ self.haste/100
+    self.GCD = 1.5 * self.castTimeMod
+
+    L.haste = format('%.2f', self.haste)
+    L.focusregen = format('%.2f', self.powerRegen)
+    L.castingmod = format('%.2f', self.castTimeMod)
+  end
+
+function T:UNIT_SPELLCAST_INTERRUPTED(e, unit, spellName, rank, target, castID)
+  if not T.isEventUnit[unit] then
+    return
+  end
+  T.spellevent[unit] = {unit, spellName, rank, target, castID, nil }
+end
+
+function T:UNIT_SPELLCAST_SENT(e,unit, spellName, rank, target, castID)
+  if not T.isEventUnit[unit] then
+    return
+  end
+  T.unit[unit].spellEventString = e
+  T.unit[unit].spellevent = {unit, spellName, rank, target, castID}
+end
+
+function T:UNIT_SPELLCAST (e, unit, spellName, rank, castID, spellID)
+  if not T.isEventUnit[unit] then
+    return
+  end
+  --name, subText, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible
+  local q = {UnitCastingInfo(unit)}
+  T.unit[unit].casting = q[1] and q or false
+  T.unit[unit].spellEventString = e
+  T.unit[unit].spellevent = {unit, spellName, rank, nil,castID, spellID }
+  print(e, unit)
+end
+
+function T:UNIT_CHANNEL(e, unit, spellName, rank, castID, spellID)
+  if not T.isEventUnit[unit] then
+    return
+  end
+  --name, subText, text, texture, startTime, endTime, isTradeSkill, notInterruptible
+  local q = {UnitChannelInfo(unit) }
+  T.unit[unit].channeling = q[1] and q or false
+  T.unit[unit].spellEventString = e
+  T.unit[unit].spellevent = {unit, spellName, rank, nil, castID, spellID }
+  print(e, unit)
+end
+
+T.UNIT_SPELLCAST_START = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_STOP = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_DELAYED = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_INTERRUPTED = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_FAILED = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_SUCCEEDED = T.UNIT_SPELLCAST
+T.UNIT_SPELLCAST_CHANNEL_START = T.UNIT_CHANNEL
+T.UNIT_SPELLCAST_CHANNEL_UPDATE= T.UNIT_CHANNEL
+T.UNIT_SPELLCAST_CHANNEL_STOP= T.UNIT_CHANNEL
+
+local UnitExists, GetRealmName, UnitClassification, UnitClass, UnitIsFriend, UnitIsEnemy, UnitIsFeignDeath
+    = UnitExists, GetRealmName, UnitClassification, UnitClass, UnitIsFriend, UnitIsEnemy, UnitIsFeignDeath
+function T:UnitChanged(e, unit)
+  local u = T.unit[unit]
+  local exists = UnitExists(unit)
+  local realm
+  _G.print('Update', cText('  '..unit..' change:'), cText(unit), cText(exists and UnitName(unit)))
+
+  u.exists = exists
+  u.name, realm = UnitName(unit)
+  u.realm = realm or GetRealmName()
+  u.classification = UnitClassification(unit)
+  u.class = UnitClass(unit)
+  u.isFriendly = UnitIsFriend('player', unit)
+  u.isEnemy = UnitIsEnemy('player', unit)
+  u.isFeign = UnitIsFeignDeath(unit)
+  u.isNeutral = not(u.isHostile or u.isFriendly)
+
+  local castinfo, channelinfo = {UnitCastingInfo(unit)}, {UnitChannelInfo(unit)}
+  u.casting = (u.exists and castinfo[1]) and castinfo or false
+  u.channeling = (u.exists and channelinfo[1]) and channelinfo or false
+  u.isCasting = (u.casting or u.channeling) and true or false
+
+
+  for k,v in pairs(T.unit[unit]) do
+    _G.print('Update', cText('  -'), cText(k),'->', cText(v))
+  end
+end
+function T:PLAYER_TARGET_CHANGED(e)
+  T:UnitChanged(e, 'target')
+end
+function T:PLAYER_FOCUS_CHANGED(e)
+  T:UnitChanged(e, 'focus')
+end
+function T:UNIT_PET(e, unit,...)
+  if unit == 'player' then
+    print(e, unit, ...)
+    T:UnitChanged(e, 'pet')
+  end
+end
+
+local Turok_OnCombat = function(inCombat)
+  print(cText('* CombatToggle:'), cBool(inCombat))
+  PlaySoundFile(db['battle_noise'.. (inCombat and '_start' or '_end')])
+
+  for _, region in pairs(Tk.LibFog.animate_regions) do
+    if region.combatFade then
+      print(' |cFFFFFF00+|r', region:GetName())
+      region:UpdateAlpha(inCombat)
+    else
+      print(' |cFFFF4400-|r', region:GetName())
+    end
+  end
+end
+
+--- InCombatLockdown() isn't updated immediately, so we have to assume
+function T:PLAYER_REGEN_DISABLED(e,...)
+  T.inCombat = true
+  Turok_OnCombat(true)
+end
+function T:PLAYER_REGEN_ENABLED(e,...)
+  T.inCombat = nil
+  Turok_OnCombat(false)
+end
+
+-- GIST datas
+function T:GIST_InspectReady (event, guid, unit)
+  print('GIST barf', guid, unit)
+end
+
+function T:GIST_Update(event, guid, unit, info)
+
+  if info.class_id and info.global_spec_id and info.guid and info.lku then
+    --_G.print('GIST', 'Update', unit)
+    --_G.print('GIST','  class:', info.class_id, 'specid:', info.global_spec_id, 'guid:', info.guid)
+
+    self.units[info.guid] = info
+    self.unitsBySlot[info.lku] = info
+  end
+end
+
+-- todo: re-locate these
+
+T.PLAY_MOVIE = function (e, id)
+  MovieFrame:Hide()
+  T:Print('skipped a shitty movie', e, id)
+end
+T.CINEMATIC_START = function(...)
+  CinematicFrame_CancelCinematic()
+  T:Print('skipped a shitty cinematic', ...)
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Turok.toc	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,13 @@
+## Interface: 60200
+## Title: Turok - Dinosaur HUD
+## Notes: General UI supplement for the raiding hunter.
+## Author: Krakyn
+## Version: 1.0-@project-revision@
+## SavedVariables: TurokData
+## OptionalDeps: Ace3, LibGroupInSpeCT-1.1, LibSharedMedia-3.0
+## X-Category: Interface Enhancements, Hunter, Combat
+## X-Website: http://www.curse.com/addons/wow/turok
+## DefaultState: Enabled
+## LoadOnDemand: 0
+
+Turok.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Turok.xml	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,26 @@
+<Ui>
+  <!--@no-lib-strip@-->
+  <Script file="!Libs\LibStub\LibStub.lua" />
+  <Include file="!Libs\AceConsole-3.0\AceConsole-3.0.xml" />
+  <Include file="!Libs\AceAddon-3.0\AceAddon-3.0.xml" />
+  <Include file="!Libs\AceConfig-3.0\AceConfig-3.0.xml" />
+  <Include file="!Libs\AceEvent-3.0\AceEvent-3.0.xml" />
+  <Include file="!Libs\AceTimer-3.0\AceTimer-3.0.xml" />
+  <Include file="!Libs\CallbackHandler-1.0\CallbackHandler-1.0.xml" />
+  <Include file="!Libs\LibSharedMedia-3.0\lib.xml" />
+  <Script file="!Libs\LibGroupInSpecT-1.1\LibGroupInSpecT-1.1.lua" />
+  <!--@end-no-lib-strip-->
+
+
+  <Script file="Init.lua" />
+  <Include file="Layout\Layout.xml" />
+  <Include file="Layout\Dialog.xml" />
+  <Script file="Prototypes.lua" />
+  <Script file="Media.lua" />
+
+  <Include file="Modules\Combat\Combat.xml" />
+  <Include file="Modules\Timer\Timer.xml" />
+  <Include file="Modules\Utilities\Utilities.xml" />
+  <Script file="Turok.lua" />
+</Ui>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Turok_Config.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,323 @@
+-- User: Krakyn
+-- Created: 12/19/2015 12:18 AM
+
+local _G = _G
+local T = LibStub("AceAddon-3.0"):GetAddon("Turok")
+local TL = 'Options'
+local mod = T:NewModule(TL)
+local LSM = LibStub("LibSharedMedia-3.0")
+local ConfigDialog = LibStub("AceConfigDialog-3.0")
+--@debug
+local print = function(...) _G.print(TL, ...) end
+local db
+
+local TITLE_FONT_COLOR = '|cFF88FF44'
+local INPUT_FONT_COLOR = '|cFFFFFF00'
+local STACK_COLOR1 = '|cFFFF8800'
+local STACK_COLOR2 = '|cFFFFFF00'
+local FONTVAR_COLOR = '|cFFFFAA44'
+local FRAMEVAR_COLOR = '|cFFFFFF44'
+local PARAMETER_COLOR = '|cFF9999FF'
+local STATE_COLOR = '|cFFFF99FF'
+
+function mod:OnInitialize()
+  T:Print("config mod init")
+end
+
+function mod:OnEnable()
+  T:Print("config mod enable")
+  db = T.db
+  LibStub("AceConfig-3.0"):RegisterOptionsTable('Turok', T.MakeOptions(), {"turok"})
+  T.configDialog = ConfigDialog:AddToBlizOptions("Turok")
+end
+
+local optL = {
+  background_color = 'Background color',
+  foreground_color = 'Foreground color',
+  width = FRAMEVAR_COLOR..'Width',
+  height = FRAMEVAR_COLOR..'Height',
+  alpha = FRAMEVAR_COLOR..'Alpha',
+  alpha_ooc = STATE_COLOR..'Alpha (OOC)',
+  label_strata = FONTVAR_COLOR..'Text Strata',
+  label_font = FONTVAR_COLOR..'Font',
+  label_size = FONTVAR_COLOR..'Font Size',
+  label_inset = FONTVAR_COLOR..'Text Insets',
+  foreground_blend = 'Foreground Blend Mode',
+  background_blend = 'Background Blend Mode',
+  isStatic = PARAMETER_COLOR..'Static Bar',
+  duration = PARAMETER_COLOR..'Duration',
+  isIcon = PARAMETER_COLOR..'Icon Only',
+}
+
+local anchors = {
+  ['CENTER'] = 'Center',
+  ['TOPLEFT'] = 'Top Left',
+  ['TOP'] = 'Top',
+  ['TOPRIGHT'] = 'Top Right',
+  ['RIGHT'] = 'Right',
+  ['BOTTOMRIGHT'] = 'Bottom Right',
+  ['BOTTOM'] = 'Bottom',
+  ['BOTTOMLEFT'] = 'Bottom Left',
+  ['LEFT'] = 'Left'
+}
+
+local frames = {
+  ['UIParent'] = 'UIParent',
+  ['TkPowerBar'] = 'Power Bar',
+  ['TekplayerCastBar'] = 'Player Castbar',
+  ['TektargetCastBar'] = 'Target Castbar',
+  ['TekfocusCastBar'] = 'Focus Castbar',
+  ['TekpetCastBar'] = 'Pet Castbar'
+}
+
+local range = {
+  ['alpha'] = {0, 1, .01},
+  ['alpha_ooc'] = {0, 1, .01},
+  ['label_size'] = {1, 72, 1},
+  ['label_inset'] = {-30, 30, 1},
+  ['width'] = {0, 1200, 1},
+  ['height'] = {0, 1200, 1},
+  ['raidbuff_width'] = {0, 1200, 1},
+  ['raidbuff_height'] = {0, 1200, 1},
+  ['duration'] = {0, 6000, 0.1 },
+  ['glow_size'] = {0, 30, 1},
+  ['spark_size'] = {0, 30, 1},
+}
+
+local opt_order = {
+  ['width'] = 10,
+  ['height'] = 15,
+  ['anchor'] = 20,
+  ['anchorTo'] = 25,
+  ['parent'] = 30,
+
+  ['alpha'] = 110,
+  ['alpha_ooc'] = 115,
+  ['label_size'] = 200,
+  ['label_inset'] = 210,
+  ['label_font'] = 220,
+
+  ['background_texture'] = 300,
+  ['background_color'] = 320,
+  ['foreground_texture'] = 350,
+  ['foreground_color'] = 370,
+}
+local opt_width = {
+
+}
+
+-- options dialog root
+T.myopts = {
+  type = 'group',
+  name =  TITLE_FONT_COLOR..'Turok|r',
+  desc = 'Dinosaur HUD',
+  handler = T,
+  set = function(info,value, ...)
+    local db = T.db
+    local index = db
+    local traversal = 'db'
+    for i = 1, #info-1 do
+      if type(index[info[i]]) == 'table' then
+        --print('|cFFFF0000SET|r:', i, info[i])
+        traversal = traversal .. '.' .. info[i]
+        index = index[info[i]]
+      end
+
+    end
+    --print('|cFFFF0000SET|r: hops=',#info,'index=',  traversal, ' key=', info[#info])
+
+    if type(value) ~= 'boolean' and select('#',...) == 3 then
+      --print('|cFFFF0000SET|r', 'multi-args', select('#',...))
+      value = {value, ... }
+    else
+      --print('|cFFFF0000SET|r', 'single-arg', value)
+    end
+
+    index[info[#info]] = value
+  end,
+  get = function(info)
+    local db = T.db
+    local value = db[info[1]]
+    local traversal = info[1]
+    if #info > 1 then
+      for i=2, #info do
+        traversal = traversal .. '.' .. info[i]
+        if value[info[i]] ~= nil then
+          value = value[info[i]]
+        end
+      end
+    end
+    --print('|cFFFF00FFGET|r: hops=', #info, 'index=',  traversal, 'value=', value)
+
+    if type(value) == 'table' then
+      return unpack(value)
+    end
+    return value
+  end,
+}
+
+function T:MakeOptions(dbtable, index, name, prefix)
+  -- index  = option insertion destination
+  -- parent = nesting point
+  local parent
+  if not (index and dbtable) then
+    dbtable = db
+    name = 'root'
+    prefix = ''
+    parent = T.myopts
+    parent.args = {
+      main = {
+        order = 1,
+        type = 'group',
+        name = 'Global',
+        get = function(info) print('db.'..info[#info]..' get')
+        if type(db[info[#info]]) == 'table' then
+          print('getting color data', unpack(db[info[#info]]))
+          return unpack(db[info[#info]])
+        end
+        return db[info[#info]]
+        end,
+        set = function(info, value, ...) print('db.'..info[#info]..' SET')
+        if select('#',...) == 3 then
+          print('receiving color data', value, ...)
+          value = {value, ...}
+        end
+        db[info[#info]] = value
+        end,
+        args = {},
+      }}
+    index = parent.args.main
+    print(STACK_COLOR1..'using TurokData as index')
+  else
+    index.type = 'group'
+    index.name = name
+    index.args = {}
+    parent = index
+  end
+
+  local order = 2
+  for k, v in pairs(dbtable) do
+    k = tostring(k)
+    local kt = INPUT_FONT_COLOR .. (optL[k] or k)
+    local nested = false
+    --@debug@
+    print(STACK_COLOR2..prefix..'.'..STACK_COLOR1..k..'|r =',v)--@debug@
+    if type(v) == 'table' then
+      if table.getn(v) == 4 then
+        index.args[k] = {
+          name = kt,
+          type = 'color',
+          hasAlpha = true,
+          --@debug@
+          print('color_pack=',unpack(v))--@debug@
+        }
+      else
+        parent.args[k] = {}
+        nested = true
+        T:MakeOptions(v, parent.args[k], k, prefix .. '.' .. name)
+      end
+    elseif type(v) == 'string' then
+      if string.match(k, "_font") then
+        local font_list = {}
+        for k, v in pairs(LSM:HashTable('font')) do
+          font_list[v] = k
+        end
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = font_list
+        }
+      elseif k:match('anchor$') or k:match('_point$') or k:match('anchorTo') then
+
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = anchors
+        }
+
+      elseif k:match('justifyH$') then
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = {
+            ['LEFT'] = 'Left',
+            ['CENTER'] = 'Center',
+            ['RIGHT'] = 'Right'
+          }
+        }
+
+      elseif k:match('strata$') then
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = {
+            BACKGROUND = 'BACKGROUND (clickthrough)',
+            LOW = 'LOW',
+            MEDIUM = 'MEDIUM',
+            HIGH = 'HIGH',
+            DIALOG = 'DIALOG',
+            FULLSCREEN = 'FULLSCREEN',
+            FULLSCREEN_DIALOG = 'FULLSCREEN_DIALOG',
+            TOOLTIP = 'TOOLTIP (clickthrough)',
+          }
+        }
+
+      elseif k:match('outline$') then
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = {
+            ['NONE'] = 'NONE',
+            ['OUTLINE'] = 'OUTLINE',
+            ['THICKOUTLINE'] = 'THICKOUTLINE'
+          }
+        }
+      elseif k:match('parent') then
+        index.args[k] = {
+          name = kt,
+          type = 'select',
+          values = frames
+        }
+      else
+
+        index.args[k] = {
+          name = kt,
+          type = 'input',
+          width = 'full',
+        }
+      end
+    elseif type(v) == 'number' then
+
+      if not range[k] then
+        range[k] = {0,150,1 }
+        print('missing range values for', k)
+      end
+
+      index.args[k] ={
+        name = kt,
+        type = 'range',
+        min = range[k][1],
+        max = range[k][2],
+        softMax = range[k][2],
+        step = range[k][3],
+        bigStep = range[k][3],
+      }
+    elseif type(v) == 'boolean' then
+
+      index.args[k] = {
+        name = kt,
+        type = 'toggle',
+      }
+    end
+    if nested then
+      parent.args[k].order = order + (opt_order[k] and opt_order[k] or 0)
+    elseif index.args[k] then
+      index.args[k].order = order + (opt_order[k] and opt_order[k] or 0)
+      print('  '..kt..' order = '..index.args[k].order)
+    else
+      print('|cFF99FFFFSkipped|r', kt)
+    end
+  end
+
+  return parent
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/readme.txt	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,42 @@
+Turok: Dinosaur HUD
+
+Hardcode of various WeakAura configurations. This is created as a personal learning challenge, so it may or may not be faster than the actual jumble of WeakAuras because that addon is written very well.
+
+Project Goals:
+
+ - Focus bar with dynamic coloring and prediction lines based on talent effects
+ - Unit cast bars that are thematically linked with everything else
+ - Dynamically arranged tracking icons and sliders cooldown/aura information
+ - Quality-of-life alerts for Lone Wolf and Aspect of the Pack
+
+Coding Conventions:
+
+ Although AceAddon modules are used, the addon is not truly modular.
+ The structure provides simple control over the internal loading sequence.
+ So if needed, "module" enables can be scattered over several post-load frames to make in-combat /reload less hazardous.
+
+ SavedVars data is arranged in a non-propagating hierarchical metatable.
+ Accessing a deeply-nested member will traverse its parents until one of them contains data.
+ The only exception to this is on lazy read expressions, such as:
+
+   > if (childtable.unsetvar) then
+
+ This will always yield a false, whether or not a value is assigned in its parents.
+ To check for hierarchical flag variables, you can compare the index against nil, like so:
+
+   > if (childtable.unsetvar ~= nil) then
+
+ The operation will trigger __index and a value from the hierarchy will be presented.
+ Any positive flag state will be copied down when direct assignment is used.
+ To get a useful flag state, you need to run the first logical expression within a ternary idiom:
+
+   > childflag = (childtable.unsetvar) and true or nil    -- pulls the true flag value
+   > parentflag = childtable.unsetvar                     -- pulls any non-nil value
+
+ Another thing to note is hierarchical values are not assigned downstream.
+ If an empty child value is indexed, it will always traverse up the tree.
+ As a result, values consulted frequently will be computationally taxing.
+ These should be copied into a local variable and read from that instead.
+ Then, when an event that might change the value is detected, the local variable is updated instead.
+ From a performance standpoint, the effects are net-positive, since assignments potentially cover dozens of keys.
+ The memory footprint also isn't much different, while the assignment operations are carried out in spaced intervals.
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok_Config/Turok_Config.toc	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,9 @@
+## Interface: 60200
+## Title: Turok Options
+## Notes: Option Panel
+## Author: Krakyn
+## Version: 1.0
+## Dependencies: Turok
+## LoadOnDemand: 1
+
+..\Turok\Turok_Config.lua
\ No newline at end of file
--- a/readme.txt	Tue Dec 15 10:10:22 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-Turok: Dinosaur HUD
-
-Hardcode of various WeakAura configurations. This is created as a personal learning challenge, so it may or may not be faster than the actual jumble of WeakAuras because that addon is written very well.
-
-Project Goals:
-
- - Focus bar with dynamic coloring and prediction lines based on talent effects
- - Unit cast bars that are thematically linked with everything else
- - Dynamically arranged tracking icons and sliders cooldown/aura information
- - Quality-of-life alerts for Lone Wolf and Aspect of the Pack
-