changeset 28:c33c17dd97e7

file renames
author Nenue
date Wed, 13 Apr 2016 20:19:37 -0400
parents c3aa94bc6be2
children adcd7c328d07
files ObjectiveTracker/Achievements.lua ObjectiveTracker/AutoQuestPopups.lua ObjectiveTracker/Data.lua ObjectiveTracker/Events.lua ObjectiveTracker/ExperienceBar.lua ObjectiveTracker/Frame.lua ObjectiveTracker/ObjectiveTracker.lua ObjectiveTracker/Quests.lua ObjectiveTracker/Widgets.lua ObjectiveTracker/Widgets.xml
diffstat 10 files changed, 2745 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Achievements.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,71 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 4/13/2016 7:48 PM
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
+local GetAutoQuestPopUp, GetQuestLogCompletionText = GetAutoQuestPopUp, GetQuestLogCompletionText
+local Cheevs = T.Cheevs
+local format = format
+local print = B.print('Tracker')
+local lprint = B.print('Line')
+local iprint = B.print('Info')
+
+
+
+Cheevs.UpdateLine = function(handler, block, line, data)
+  local print = B.print('CheevsLine')
+  line.progress = 0
+  print('  ', data.objectiveIndex,'|cFF0088FF-|r', data.objectiveType, data.text)
+  if data.flags then
+    if band(data.flags, 0x00000001) > 0 then
+      line.format = "%d/%d"
+      line.widget = T.SetWidget(line, data, 'ProgressBar', data.criteriaID)
+      line.height = line.widget.height
+    elseif band(data.flags, 0x00000002) then
+      line.widget = nil
+    else
+      line.widget = nil
+      line.displayColor = 'FFFFFF'
+      line.displayText = line.text
+
+    end
+  else
+
+    line.displayText = data.text
+  end
+  print('line.type =', data.type)
+  print('  ** qtyStr:', data.quantityString, 'qty:', data.quantity, 'assetID:', data.assetID)
+end
+
+Cheevs.Select = function(self)
+  --mod:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT)
+end
+Cheevs.Remove = function(self)
+
+  RemoveTrackedAchievement(self.info.cheevID)
+end
+Cheevs.OnMouseUp = function(self, button)
+
+  Default.OnMouseUp(self, button)
+end
+Cheevs.Link = function(self)
+
+  local achievementLink = GetAchievementLink(self.info.cheevID);
+  if ( achievementLink ) then
+    ChatEdit_InsertLink(achievementLink);
+  end
+end
+
+Cheevs.Open = function(self)
+
+  if ( not AchievementFrame ) then
+    AchievementFrame_LoadUI();
+  end
+  if ( not AchievementFrame:IsShown() ) then
+    AchievementFrame_ToggleAchievementFrame();
+  end
+  AchievementFrame_SelectAchievement(self.info.cheevID);
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/AutoQuestPopups.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,34 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 4/13/2016 7:49 PM
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
+local ShowQuestOffer, ShowQuestComplete, RemoveAutoQuestPopUp = ShowQuestOffer, ShowQuestComplete, RemoveAutoQuestPopUp
+local lprint = B.print('Line')
+local AutoQuest = T.AutoQuest
+
+--- They are not exactly quests, but the aliases used to signal that a quest was pushed into your log by the environment.
+-- Once accepted they never appear again unless the quest is dropped (daily reset/abandon quest)
+local CLICK_TO_COMPLETE = 'Click to Complete'
+local CLICK_TO_ACCEPT = 'Click to Accept'
+AutoQuest.UpdateObjectives = function(handler, block)
+  local print = lprint
+  if block.info.type == 'OFFER' then
+    block.status:SetText(CLICK_TO_ACCEPT)
+  end
+end
+
+AutoQuest.Select = function(self)
+
+  if self.info.popupType == 'OFFER'  then
+    ShowQuestOffer(self.info.questIndex)
+  else
+    ShowQuestComplete(self.info.questIndex)
+  end
+  RemoveAutoQuestPopUp(self.info.questID)
+end
+
+AutoQuest.Link = T.Quest.Link
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Data.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,341 @@
+local B = select(2,...).frame
+local wipe, pairs, ipairs, min, max, unpack = table.wipe, pairs, ipairs, min, max, unpack
+local tinsert, tostring, format, mod = tinsert, tostring, format, mod
+local GetQuestTagInfo, GetQuestLogTitle = GetQuestTagInfo, GetQuestLogTitle
+local GetNumQuestLogEntries, GetNumQuestWatches, GetQuestLogCompletionText, IsQuestWatched, IsQuestHardWatched, GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown = GetNumQuestLogEntries, GetNumQuestWatches, GetQuestLogCompletionText, IsQuestWatched, IsQuestHardWatched, GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown
+local GetNumAutoQuestPopUps, GetAutoQuestPopUp, GetTasksTable, GetNumQuestLogTasks, GetTaskInfo, GetQuestObjectiveInfo = GetNumAutoQuestPopUps, GetAutoQuestPopUp, GetTasksTable, GetNumQuestLogTasks, GetTaskInfo, GetQuestObjectiveInfo
+local GetNumQuestLogRewardCurrencies, GetQuestLogRewardCurrencyInfo, GetNumQuestLogRewards, GetQuestLogRewardInfo, GetQuestLogRewardMoney, GetMoneyString = GetNumQuestLogRewardCurrencies, GetQuestLogRewardCurrencyInfo, GetNumQuestLogRewards, GetQuestLogRewardInfo, GetQuestLogRewardMoney, GetMoneyString
+local GetNumQuestLeaderBoards, GetAchievementNumCriteria, GetQuestLogLeaderBoard, GetAchievementCriteriaInfo = GetNumQuestLeaderBoards, GetAchievementNumCriteria, GetQuestLogLeaderBoard, GetAchievementCriteriaInfo
+local GetQuestWatchIndex, GetQuestLogIndexByID, GetSuperTrackedQuestID, SetSuperTrackedQuestID, GetQuestWatchInfo = GetQuestWatchIndex, GetQuestLogIndexByID, GetSuperTrackedQuestID, SetSuperTrackedQuestID, GetQuestWatchInfo
+local QuestHasPOIInfo, GetDistanceSqToQuest, GetQuestFactionGroup = QuestHasPOIInfo, GetDistanceSqToQuest, GetQuestFactionGroup
+local GetTrackedAchievements, GetNumTrackedAchievements, GetAchievementInfo = GetTrackedAchievements, GetNumTrackedAchievements, GetAchievementInfo
+local GetMoney, floor = GetMoney, floor
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('Info')
+local QUEST_TAG_DUNGEON = QUEST_TAG_DUNGEON
+local QUEST_TAG_GROUP = QUEST_TAG_GROUP
+local QUEST_TAG_ACCOUNT = QUEST_TAG_ACCOUNT
+local QUEST_TAG_TCOORDS = QUEST_TAG_TCOORDS
+local LE_QUEST_FREQUENCY_DAILY = LE_QUEST_FREQUENCY_DAILY
+local LE_QUEST_FREQUENCY_WEEKLY = LE_QUEST_FREQUENCY_WEEKLY
+local FACTION_ALLIANCE, LE_QUEST_FACTION_HORDE, FACTION_HORDE, LE_QUEST_FACTION_HORDE = FACTION_ALLIANCE, LE_QUEST_FACTION_HORDE, FACTION_HORDE, LE_QUEST_FACTION_HORDE
+
+local Tracker, Bonus, AutoQuest, Quest, Cheevs = T.DefaultTracker, T.Bonus, T.AutoQuest, T.Quest, T.Cheevs
+--------------------------------------------------------------------
+--- Tracker-specific data retrieval functions
+--------------------------------------------------------------------
+
+
+local DoQuestRewards= function(t, questID)
+  local rewards = {}
+  t.numCurrencies = GetNumQuestLogRewardCurrencies(questID)
+  for i = 1, t.numCurrencies do
+    local name, texture, count = GetQuestLogRewardCurrencyInfo(i, questID)
+    tinsert(rewards,{
+      type = 'currency',
+      index = i,
+      name = name,
+      texture = texture,
+      count = count
+    });
+  end
+  -- items
+  t.numItems = GetNumQuestLogRewards(questID)
+  for i = 1, t.numItems do
+    local name, texture, count, quality, isUsable = GetQuestLogRewardInfo(i, questID)
+    tinsert(rewards, {
+      type = 'item',
+      index = i ,
+      name = name,
+      texture = texture,
+      count = count,
+      quality = quality,
+      isUsable = isUsable
+    });
+  end
+  -- money
+
+  local money = GetQuestLogRewardMoney(questID)
+  if ( money > 0 ) then
+    tinsert(rewards, {
+      type = 'money',
+      name = GetMoneyString(money),
+      texture = "Interface\\Icons\\inv_misc_coin_01",
+      count = 0,
+    });
+  end
+
+  if #rewards >= 1 then
+    t.rewardInfo = rewards
+  end
+end
+
+
+-----------------------------
+--- AUTO_QUEST
+AutoQuest.LogInfo = {}
+AutoQuest.LogBlock = {}
+AutoQuest.QuestBlock = {}
+AutoQuest.WatchBlock = {}
+function AutoQuest:GetNumWatched ()
+  print(self.name, self)
+  Quest:GetNumWatched()
+  self.numWatched = GetNumAutoQuestPopUps()
+
+  return self.numWatched
+end
+AutoQuest.GetInfo = function(self, popupIndex)
+
+
+  local questID, type = GetAutoQuestPopUp(popupIndex)
+  local questLogIndex = GetQuestLogIndexByID(questID)
+  local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questLogIndex)
+
+  self.Info[questID] = self.Info[questID] or {}
+  local popup = self.Info[questID]
+  popup.title = title
+  popup.description = type
+  popup.popupType = type
+  popup.questID = questID
+  popup.questLogIndex = questLogIndex
+  popup.popupIndex = popupIndex
+
+  self.Info[questID] = popup
+  self.WatchInfo[popupIndex] = popup
+
+
+  return self.Info[questID]
+end
+
+-----------------------------
+--- BONUS OBJECTIVE
+-- The default UI pops them up as you enter their relevant areas, but the data is actually available at all times.
+-- The only requirement is that you've been to said area and progressed any of the objectives.
+-- Blizzard deal with this fact by caching any task data collected during session and masking out whatever gets completed.
+-- For the addon's module structure to work, GetNumWatched method also invokes a tasks table scan.
+-- That composes the table searched by GetInfo().
+
+------------------------------------------------------------------------------------------
+--- These functions are copied from Blizzard_BonusObjectiveTracker.lua;
+-- It's kind of dumb, but this avoids the risk of code taint.
+
+--- Returns a tasks table modified to include recently completed objectives
+local UnitName, GetRealmName = UnitName, GetRealmName
+local InternalGetTasksTable = function()
+  local savedTasks = T.Conf.TasksLog
+  local char = UnitName("player")
+  local realm = GetRealmName()
+  local tasks = GetTasksTable()
+
+  for questID, data in pairs(Bonus.Info) do
+
+    print('  -- questID:', questID, #data.objectives)
+      for i, o in ipairs(data.objectives) do
+        print('    --', i, o.text)
+      end
+
+  end
+
+
+  for questID, data in pairs(savedTasks) do
+    if questID > 0  then
+      local found = false
+      for i = 1, #tasks do
+        if tasks[i] == questID then
+          found = true
+          break
+        end
+      end
+      -- if it's not part of the current table, then try to insert it where it was last found
+      if not found then
+        if data.watchIndex < #tasks then
+          tinsert(tasks, data.watchIndex, data)
+        else
+          tinsert(tasks, data)
+        end
+      end
+    end
+  end
+  return tasks
+end
+
+--- Returns an entry from the composed tasks table if possible, otherwise makes an API pull
+local InternalGetTaskInfo = function(questID)
+  local completedTasks = T.Conf.TasksLog
+  if completedTasks[questID] then
+    return true, true, #completedTasks[questID].objectives
+  else
+    return GetTaskInfo(questID)
+  end
+end
+
+--- Same as above but for the objective entries
+local InternalGetQuestObjectiveInfo = function(questID, objectiveIndex)
+  local completedTasks = T.Conf.TasksLog
+  if ( completedTasks[questID] ) then
+    print('using internal data')
+    return completedTasks[questID].objectives[objectiveIndex], completedTasks[questID].objectiveType, true;
+  else
+    return GetQuestObjectiveInfo(questID, objectiveIndex, false);
+  end
+end
+
+--- end redundant copy of silliness
+------------------------------------------------------------------------------------------
+
+Bonus.Completed = {}
+Bonus.POI = {}
+Bonus.Scenario = {}
+Bonus.QuestBlock = {}
+Bonus.WatchInfo = {}
+function Bonus:GetNumWatched ()
+  print(self.name, self)
+
+  local tasks = InternalGetTasksTable()
+  local numWatched = 0
+  local numAll = 0
+  self.WatchInfo = {}
+  print('|cFFFFFF00Bonus.GetNumWatched()|r', #tasks)
+  print('  TasksTable pull:')
+  for i, questID in ipairs(tasks) do
+    local isInArea, isOnMap, numObjectives = InternalGetTaskInfo(questID)
+    local existingTask = self.QuestBlock[questID]
+    local displayObjectiveHeader = false;
+    local test = (isInArea or (isOnMap and existingTask))
+    --local test = true
+    if test then
+      self.Info[questID] = self.Info[questID] or {}
+
+      local t = self.Info[questID]
+      self.WatchInfo[i] = t
+      t.isInArea = isInArea
+      t.isOnMap = isOnMap
+      t.existingTask = existingTask
+      t.questID = questID
+      t.objectives = {}
+      t.taskIndex = i
+
+
+      DoQuestRewards(t, questID)
+
+      local taskTitle
+      local taskFinished = true;
+      for objectiveIndex = 1, numObjectives do
+        local text, objectiveType, finished, displayAsObjective = InternalGetQuestObjectiveInfo(questID, objectiveIndex, false);
+        displayObjectiveHeader = displayObjectiveHeader or displayAsObjective;
+        if not taskTitle then
+          if objectiveType == 'progressbar' and not text:match('^%d%+\\%d+') then
+            taskTitle = text
+            text = ''
+          end
+        end
+
+
+        print('  --', text, objectiveType, finished, displayAsObjective)
+        t.objectives[objectiveIndex] = t.objectives[objectiveIndex] or  {}
+        local  o = t.objectives[objectiveIndex]
+
+        o.objectiveIndex = objectiveIndex
+        o.text = text
+        o.objectiveType = objectiveType
+        o.finished = finished
+        o.displayAsObjective = displayAsObjective
+        print('     |cFF00FF88*', objectiveIndex, text)
+      end
+
+      -- didn't get a name from progress bar? what about area name
+      if not taskTitle then
+        if isInArea then
+          taskTitle = GetMapNameByID(GetCurrentMapAreaID())
+        end
+      end
+      t.title = taskTitle
+    end
+
+    print ('    |cFF00FF88#', i, 'questID', questID, 'inArea', isInArea, 'onMap', isOnMap, 'existing', (existingTask and 'Y' or 'N'), (test and '|cFF00FF00show|r' or '|cFFFF0088hide|r'))
+  end
+
+
+  self.numAll = #tasks
+  self.numWatched = #self.WatchInfo
+  print('  stats:', self.numAll, 'active tasks,', self.numWatched, 'nearby or animating')
+  --return #tasks
+  return #self.WatchInfo
+end
+
+--- info cleanup done when  turn-ins are detected
+Bonus.OnTurnIn = function(self, questID, xp, money)
+
+  if #self.info.rewardInfo >= 1 then
+    for i, reward in ipairs(self.info.rewardInfo) do
+      --[[
+        type = 'item',
+        index = i ,
+        name = name,
+        texture = texture,
+        count = count,
+        quality = quality,
+        isUsable = isUsable
+       ]]
+      print(' reward ', i, ' ', reward.type, reward.name, reward.count)
+
+    end
+  end
+
+  print('|cFFFF8800'..self.name..':OnTurnIn call', questID, xp, money)
+  local savedTasks = B.Conf.TasksLog
+
+  self.Info[questID].completedTime = GetTime()
+  self.Info[questID].animate = true
+  T.SetAnimate(self.watchReasonModule)
+  savedTasks[questID] = self.Info[questID]
+end
+
+Bonus.GetInfo = function(self, taskIndex)
+  print(self.name, self)
+  return self.WatchInfo[taskIndex]
+end
+
+
+
+Cheevs.GetNumWatched = function(self)
+  print('|cFF00FF00' .. GetTime())
+  Cheevs.trackedCheevs = {GetTrackedAchievements()}
+  return GetNumTrackedAchievements()
+end
+Cheevs.GetInfo = function(self, index)
+  local cheevID = Cheevs.trackedCheevs[index]
+  local id, name, points, completed, month, day, year, description, flags, icon, rewardText, isGuildAch, wasEarnedByMe, earnedBy = GetAchievementInfo(cheevID)
+
+  self.Info[cheevID] = {}
+  local c = self.Info[cheevID]
+  c.type = 'Cheevs'
+  c.watchIndex = index
+  c.cheevID = cheevID
+  c.title = name
+  c.points, c.completed, c.month, c.day, c.year, c.description, c.flags, c.icon, c.rewardText, c.isGuildAch, c.wasEarnedByMe, c.earnedBy =
+  points, completed, month, day, year, description, flags, icon, rewardText, isGuildAch, wasEarnedByMe, earnedBy
+  c.numObjectives = GetAchievementNumCriteria(cheevID)
+  c.objectives = {}
+  for i = 1, c.numObjectives do
+    local description, type, completed, quantity, requiredQuantity, characterName, flags, assetID, quantityString, criteriaID = GetAchievementCriteriaInfo(cheevID, i)
+    c.objectives[i] = {
+      objectiveIndex = i,
+      cheevID = cheevID,
+      text = description,
+      type = type,
+      finished = completed,
+      value = quantity,
+      maxValue = requiredQuantity,
+      characterName = characterName,
+      flags = flags,
+      assetID = assetID,
+      quantityString = quantityString,
+      criteriaID = criteriaID,
+    }
+  end
+  print('Cheevs.|cFF0088FFGetInfo|r('..index..')', 'obj:', GetAchievementNumCriteria(cheevID), name, description)
+
+  self.WatchInfo[index] = c
+  return self.Info[cheevID]
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Events.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,72 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 3/30/2016 1:23 AM
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('Objectives')
+
+-------------------------------------------------------------------
+--- SecureHooked blizzard functions
+-------------------------------------------------------------------
+T.AddQuestWatch = function(questID)
+    T:Update(0x00000003)
+end
+
+local Quest = T.Quest
+T.RemoveQuestWatch = function(questIndex, ...)
+  print('|cFFFF8800RemoveQuestWatch', questIndex, ...)
+
+  local info = T.Quest.LogInfo[questIndex]
+
+  -- remove quest refs
+  local block = Quest.QuestBlock[info.questID]
+  Quest.QuestBlock[info.questID] = nil
+  Quest.LogBlock[questIndex] = nil
+
+  -- remove if they still match
+  if Quest.WatchInfo[info.watchIndex] == info then
+    print('cleaning dead WatchInfo entry')
+    Quest.WatchInfo[info.watchIndex] = nil
+  end
+
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST)
+end
+
+T.AddTrackedAchievement = function(cheevID)
+  T.CleanWidgets()
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT)
+end
+
+
+T.RemoveTrackedAchievement = function(cheevID)
+  print('|cFFFF8800UntrackAchievement', cheevID)
+  T.CleanWidgets()
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT)
+end
+
+T.AcceptQuest = function()
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST_ADDED)
+end
+
+T.AbandonQuest = function()
+  QuestPOIUpdateIcons()
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST)
+end
+T.TurnInQuest = function()
+  QuestPOIUpdateIcons()
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST)
+end
+T.AddAutoQuestPopUp = function(...)
+   T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP)
+end
+T.RemoveAutoQuestPopUp = function(...)
+  T:Update(OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP)
+end
+
+
+
+T.SetSuperTrackedQuestID = function(questID)
+  --T:Update()
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/ExperienceBar.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,92 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 4/6/2016 4:44 AM
+
+local B = select(2,...).frame
+local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local tostring = tostring
+local UnitLevel, IsResting, UnitXP, UnitXPMax, GetXPExhaustion, IsXPUserDisabled = UnitLevel, IsResting, UnitXP, UnitXPMax, GetXPExhaustion, IsXPUserDisabled
+local Wrapper = _G.VeneerObjectiveWrapper
+local print = B.print('XPTracker')
+
+mod.InitializeXPTracker = function()
+  local XPBar = Wrapper.XPBar
+  if UnitLevel('player') == 100 then
+    XPBar:Hide()
+    return
+  end
+
+  --- xp bar
+  XPBar:SetWidth(mod.Conf.Wrapper.WrapperWidth - Wrapper.CloseButton:GetWidth())
+  XPBar.statusbg:SetAllPoints(XPBar)
+  XPBar:RegisterEvent('DISABLE_XP_GAIN')
+  XPBar:RegisterEvent('ENABLE_XP_GAIN')
+  XPBar:SetScript('OnEvent', mod.UpdateXP)
+
+  if not IsXPUserDisabled() then
+    mod.EnableXP(XPBar)
+  else
+    mod.DisableXP(XPBar)
+  end
+
+  mod.UpdateXP(XPBar)
+end
+
+mod.EnableXP = function(self)
+  self:RegisterEvent('PLAYER_XP_UPDATE')
+  self:RegisterEvent('PLAYER_LEVEL_UP')
+  self:RegisterEvent('PLAYER_UPDATE_RESTING')
+  self.statusbg:SetTexture(0,0,0,.25)
+  self:Show()
+end
+
+mod.DisableXP = function(self)
+  self:UnregisterEvent('PLAYER_XP_UPDATE')
+  self:UnregisterEvent('PLAYER_LEVEL_UP')
+  self:UnregisterEvent('PLAYER_UPDATE_RESTING')
+  self.statusbg:SetTexture(0.5,0.5,0.5,0.5)
+  self:Hide()
+end
+
+mod.UpdateXP = function(self, event)
+  if event == 'DISABLE_XP_GAIN' then
+    mod.DisableXP(self)
+  elseif event == 'ENABLE_XP_GAIN' then
+    mod.EnableXP(self)
+  end
+
+  if not IsXPUserDisabled() then
+
+    local xp = UnitXP('player')
+    local xpmax = UnitXPMax('player')
+    local rest = GetXPExhaustion()
+    self.foreground:SetWidth((xp/xpmax) * self:GetWidth())
+    if rest then
+      self.rested:ClearAllPoints()
+      if xp == 0 then
+        self.rested:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 0)
+      else
+        self.rested:SetPoint('TOPLEFT', self.fg, 'TOPRIGHT', 0, 0)
+      end
+
+      if (xp + rest) > xpmax then
+        self.rested:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 0, 0)
+      else
+        self.rested:SetWidth((rest/xpmax) * self:GetWidth())
+      end
+      self.rested:SetPoint('BOTTOM', self, 'BOTTOM')
+      self.rested:Show()
+    else
+      self.rested:Hide()
+    end
+
+    if IsResting() then
+      self.statusbg:SetTexture(.2,.8,.2,.5)
+    else
+      self.statusbg:SetTexture(0,0,0,.25)
+    end
+    self.xpText:SetText(xp .. '/'.. xpmax .. (rest and (' ('..tostring(rest)..')') or ''))
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Frame.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,737 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 3/30/2016 12:49 AM
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
+local IsResting, UnitXP, UnitXPMax, GetXPExhaustion = IsResting, UnitXP, UnitXPMax, GetXPExhaustion
+local UnitLevel, IsQuestWatched, UIParent = UnitLevel, IsQuestWatched, UIParent
+local GetAutoQuestPopUp, GetQuestLogCompletionText = GetAutoQuestPopUp, GetQuestLogCompletionText
+local PERCENTAGE_STRING, GetQuestProgressBarPercent = PERCENTAGE_STRING, GetQuestProgressBarPercent
+local Default, AutoQuest, Quest, Bonus, Cheevs = T.DefaultHandler, T.AutoQuest, T.Quest, T.Bonus, T.Cheevs
+local InCombatLockdown, format, lshift, CreateFrame = InCombatLockdown, format, bit.lshift, CreateFrame
+local print = B.print('Tracker')
+local unitLevel = 1
+local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_REASON
+local debug = false
+--------------------------------------------------------------------
+--- Global frame layout
+--------------------------------------------------------------------
+
+--- Upvalues
+local Wrapper = _G.VeneerObjectiveWrapper
+local Scroller = Wrapper.scrollArea
+local Scroll = _G.VeneerObjectiveScroll
+local orderedHandlers = T.orderedHandlers
+local orderedNames = T.orderedNames
+
+--- Temp values set during updates
+local wrapperWidth, wrapperHeight
+local scrollWidth, scrollHeight
+local previousBlock
+local currentBlock
+--- todo: source these from config
+local itemButtonSize, itemButtonSpacing =  36, 1
+
+local headerHeight, headerColor, headerSpacing = 16, {1,1,1,1}, 2
+local headerbg = {'VERTICAL', 1, 1, 0.5, 0.5, 1, 1, 0.5, 0}
+local headerFont, headerSize, headerOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 14, 'OUTLINE'
+
+local titlebg = {'HORIZONTAL', 1, 0, .7, .25, 1, 0, .7, .125}
+local titlebg_daily = {'HORIZONTAL', 0, .7, 1, .25, 0, 1, .7, .125 }
+local titlebg_account = {'HORIZONTAL', 0, .45, 1, .25, 0,  .45, 1, .125}
+local titleFont, titleSize, titleOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 16, 'OUTLINE'
+
+local textbg =  {'HORIZONTAL', 0, 0, 0, 0.4, 0, 0, 0, 0 }
+local textbg_daily = {'HORIZONTAL', 0, .7, 1, .1, 0, 1, .7, .075 }
+local textbg_account = {'HORIZONTAL', 0, .45, 1, 0.4, 0,  .41, 1, .085 }
+local textFont, textSize, textOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Regular.ttf]], 16, 'OUTLINE'
+
+
+local selectionbg = {'HORIZONTAL', 1, 1, 1, 0, 1, 1, 1, 0.225}
+local titleSpacing, textSpacing, blockSpacing = 3, 3, 1
+local titleIndent, textIndent,selectionIndent = 2, 5, 50
+
+local wrapperMaxWidth, wrapperMaxHeight = 270, 490 -- these are the hard bounds, actual *Height variables are changed
+local wrapperHeadFont, wrapperHeadSize, wrapperHeadOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 16, 'NONE'
+local wrapperPosition = {'RIGHT', UIParent, 'RIGHT', -84, 0 }
+local rewardSize = 32
+local oprint = B.print('Objectives')
+local bprint = B.print('Block')
+local tprint = B.print('Tracker')
+local lprint = B.print('Line')
+
+local currentPosition, anchorFrame, anchorPoint
+
+--- Positioning and stuff
+local tick = 0
+function T:Update (reason, ...)
+  tick = tick + 1
+  local print = tprint
+  reason = reason or OBJECTIVE_TRACKER_UPDATE_REASON
+  local updateWrapper = 0
+  local hasStuff
+  local insertingStuff
+
+  print(format('%d |cFFFF%04X Update()', tick, lshift(reason, 4)), reason, ...)
+  currentPosition = 0
+  anchorPoint = 'TOP'
+  anchorFrame = Scroll
+
+  local wrapperHeight = 0
+  for id, handler in pairs(T.orderedHandlers) do
+    local frame = handler.frame
+
+    print(format('|cFF00FFFF%s and(%04X vs %04x+%04x) = %04X|r', handler.name, reason, handler.updateReasonModule, handler.updateReasonEvents, band(reason, handler.updateReasonModule + handler.updateReasonEvents)))
+    if band(reason, handler.updateReasonModule + handler.updateReasonEvents) > 0 then
+      handler:Update(reason, ...)
+      print(' |cFF00FF00'..id..'|r', handler.displayName, 'count:', handler.numWatched)
+      insertingStuff = true
+    else
+      print(' |cFFFF0088'..id..'|r', 'no reason to update')
+    end
+
+    if handler.numWatched >= 1 then
+      hasStuff = true
+      currentPosition = currentPosition + 1
+      frame:SetParent(Scroll)
+      frame:SetPoint('TOP', anchorFrame, anchorPoint, 0, 0)
+      print('  |cFF00BBFFpinning to', anchorFrame:GetName(), anchorPoint)
+      anchorFrame = handler.frame
+      anchorPoint = 'BOTTOM'
+
+      print('current frame height:', frame.height)
+      wrapperHeight = wrapperHeight + frame.height
+      print('|cFFFF0088total height:', wrapperHeight)
+    else
+      handler.frame:Hide()
+    end
+  end
+
+
+  if  hasStuff or insertingStuff then
+    print('updating height to', wrapperHeight)
+    Wrapper:SetHeight(wrapperHeight)
+    Scroller:SetHeight(wrapperHeight)
+    Scroll:SetHeight(wrapperHeight)
+    Scroller:SetVerticalScroll(B.Conf.ObjectiveScroll or 0)
+    print('|cFFFF8800Wrapper:', Wrapper:GetSize())
+    for i = 1, Wrapper:GetNumPoints() do
+      print(' ', Wrapper:GetPoint(i))
+    end
+    print('  |cFF00FFFFScroller:', Scroller:GetSize())
+    for i = 1, Scroller:GetNumPoints() do
+      print(' ', Scroller:GetPoint(i))
+    end
+    print('  |cFF00FFFFScroll:', Scroll:GetSize())
+    for i = 1, Scroll:GetNumPoints() do
+      print(' ', Scroll:GetPoint(i))
+    end
+
+    Wrapper:Show()
+    Scroller:Show()
+    Scroll:Show()
+  end
+  Quest.GetClosest()
+  --T.UpdateActionButtons(reason)
+end
+
+T.AddBlock = function(self, block)
+  local print = bprint
+  local tracker = self.frame
+  local info = block.info
+  currentBlock:SetPoint('TOPLEFT', self.currentAnchor, 'BOTTOMLEFT', 0, 0)
+  currentBlock:SetPoint('RIGHT', tracker,'RIGHT', 0, 0)
+  self.currentAnchor = currentBlock
+  print('    |cFFFFFF00'..tracker.height..'|r', '|cFF00FF00'..currentBlock:GetName()..'|r', currentBlock.height, tracker.height)
+  tracker.height = tracker.height + currentBlock.height
+  tracker.numBlocks = max(tracker.numBlocks, info.blockIndex)
+  tracker.actualBlocks = tracker.actualBlocks + 1
+end
+
+--- Used as an iterator of sorts for cascaded tag icon placements (the daily/faction/account icons)
+T.AddTag = function (block, tagName, tagPoint, tagAnchor, tagRelative)
+  local print = bprint
+  local tag = block[tagName]
+  if block.info[tagName] and tag then
+    tag:SetTexCoord(unpack(block.info[tagName]))
+    tag:Show()
+    tag:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0)
+    tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', tag, 'TOPLEFT'
+  else
+    block[tagName]:Hide()
+  end
+  return tagPoint, tagAnchor, tagRelative
+end
+
+--- Adds the given line to the current content and advances the anchor pointer to that new line for the following call.
+T.AddLine = function(block, line)
+  local print = lprint
+  line:ClearAllPoints()
+  line:SetPoint('LEFT', block, 'LEFT', 0, 0)
+  line:SetPoint('TOP', block.endPoint, 'BOTTOM', 0, -textSpacing)
+  line:SetPoint('RIGHT', block, 'RIGHT', 0, 0)
+  line:SetHeight(line.height)
+  line:Show()
+
+  block.endIndex = line.index
+  block.numLines = block.numLines + 1
+  block.attachmentHeight = block.attachmentHeight + (line.height + textSpacing)
+  print('     |cFF0088FFsetting line #'..block.numLines..' for|r', block.info.title, "\n            |cFF0088FFsize:|r", line.height,
+    "|cFF0088FFpoint:|r", line:GetPoint(1), "|cFF0088FFwidget:|r", (line.widget and 'Y' or 'N'))
+  block.endPoint = line
+end
+
+--- Creates or retrieves a complete line data object
+T.GetLine = function(handler, block, lineIndex)
+  local print = lprint
+  local blockIndex = block.index
+  if not block.lines then
+    block.lines = {}
+  end
+  local lines = block.lines
+  if not lines[lineIndex] then
+    print('     |cFF00FF88created line #'..lineIndex..' from for '..handler.name..' block #'..blockIndex)
+    lines[lineIndex] = CreateFrame('Frame', 'Vn'..handler.name .. blockIndex..'ObjectiveLine'..lineIndex, block, 'VeneerTrackerObjective')
+    local line = lines[lineIndex]
+    line.index = lineIndex
+    line.height = 0
+    line.status:SetSpacing(textSpacing)
+    line.status:SetPoint('LEFT', line, 'LEFT', textIndent, 0)
+    B.SetConfigLayers(line)
+
+    if lines[lineIndex+1] then
+      lines[lineIndex+1]:ClearAllPoints()
+    end
+
+    if debug then
+      for _, region in ipairs(lines[lineIndex].debug) do
+        region:Show()
+      end
+    end
+
+  end
+  return lines[lineIndex]
+end
+
+--- Creates or retrieves a complete block frame object
+T.GetBlock = function(handler, blockIndex)
+  local print = bprint
+  local block = handler.usedBlocks[blockIndex]
+
+  if not handler.usedBlocks[blockIndex] then
+    if #handler.freeBlocks >= 1 then
+      block = handler.freeBlocks[#handler.freeBlocks]
+      handler.freeBlocks[#handler.freeBlocks] = nil
+    else
+      block = CreateFrame('Frame', 'Veneer'..tostring(handler)..'Block'..blockIndex, Scroll, 'VeneerTrackerBlock')
+
+
+
+      local c = T.Conf.Wrapper
+      block.index = blockIndex
+      block.SetStyle = T.SetBlockStyle
+      block:SetWidth(c.Width)
+
+      block.title:SetSpacing(c.TitleSpacing)
+      block.title:SetPoint('TOP', block, 'TOP', 0, -titleSpacing)
+
+      block.titlebg:SetTexture(1,1,1,1)
+      block.titlebg:SetGradientAlpha(unpack(T.colors.default.titlebg))
+      block.titlebg:SetPoint('TOP', block, 'TOP', 0, 0)
+      block.titlebg:SetPoint('BOTTOM', block.title, 'BOTTOM', 0, -titleSpacing)
+
+      block.status:SetSpacing(c.TextSpacing)
+      block.status:SetPoint('TOP', block.titlebg, 'BOTTOM', 0, -textSpacing)
+      block.status:SetPoint('LEFT', block.titlebg, 'LEFT', textIndent, 0)
+
+      block.statusbg:SetPoint('TOP', block.titlebg, 'BOTTOM', 0, 0)
+      block.statusbg:SetPoint('BOTTOM', block, 'BOTTOM', 0, 0)
+      block.statusbg:SetTexture(1,1,1,1)
+      block.statusbg:SetGradientAlpha(unpack(T.colors.default.textbg))
+
+      block.SelectionOverlay:SetGradientAlpha(unpack(T.colors.default.selectionbg))
+      block.SelectionOverlay:SetPoint('TOPLEFT', selectionIndent, 0)
+      block.SelectionOverlay:SetPoint('BOTTOMRIGHT')
+
+      block.icon:SetSize(rewardSize, rewardSize)
+      block.icon:SetPoint('TOPRIGHT', block, 'TOPRIGHT', -2, -2)
+
+
+      --- methods for event handlers
+
+      block.Select = handler.Select
+      block.Open = handler.Open
+      block.Remove = handler.Remove
+      block.Link = handler.Link
+      block.clickZone:SetScript('OnMouseUp', function(self, ...) handler.OnMouseUp(block, ...) end)
+      block.clickZone:SetScript('OnMouseDown', function(self, ...) handler.OnMouseDown(block, ...) end)
+      block.attachmentHeight = 0
+      block:ClearAllPoints()
+
+      B.SetConfigLayers(block)
+
+      if debug then
+        for _, region in ipairs(block.debug) do
+          region:Show()
+        end
+      end
+    end
+    handler.usedBlocks[blockIndex] = block
+  end
+  return handler.usedBlocks[blockIndex]
+end
+
+--- Content generator base
+Default.Update = function (self, reason, ...)
+  local print = tprint
+  local tracker = self.frame
+  local blockIndex = 0
+  tracker.previousHeight = tracker.height
+  tracker.height = 0
+
+  tracker.title:SetFont(headerFont, headerSize, headerOutline)
+  tracker.titlebg:SetHeight(headerHeight)
+  tracker.title:SetTextColor(unpack(headerColor))
+
+  self.currentAnchor = tracker.titlebg
+  local numWatched = self:GetNumWatched()
+  local numBlocks = self.numBlocks
+  local actualBlocks = 0
+  for watchIndex = 1, 25 do
+    blockIndex = blockIndex + 1
+    if watchIndex <= numWatched then
+      local info = self:GetInfo(watchIndex)
+      if info then
+        local currentBlock = self:UpdateBlock(blockIndex, info)
+        T.AddBlock(currentBlock)
+      else
+        print('    |cFFFF0000bad GetInfo data for #'..watchIndex)
+      end
+    elseif watchIndex <= numBlocks then
+      local used = self.usedBlocks
+      local free = self.freeBlocks
+      print('clean up dead quest block')
+      if used[blockIndex] then
+        used[blockIndex]:Hide()
+        used[blockIndex]:ClearAllPoints()
+        free[#free+1]= used[blockIndex]
+        used[blockIndex] = nil
+      end
+    else
+      print('  |cFFFF9900END|r @', blockIndex)
+      break -- done with quest stuff
+    end
+  end
+
+  self.numWatched = numWatched
+  self.numBlocks = numBlocks
+  self.actualBlocks = actualBlocks
+
+  if numBlocks >= 1 then
+    tracker.height = tracker.height + headerHeight
+    tracker:Show()
+
+    if tracker.wasEmpty then
+      tracker.headerFade:Play()
+      tracker.wasEmpty = nil
+    end
+    if tracker.height ~= tracker.previousHeight then
+      tracker:SetHeight(tracker.height)
+    end
+  else
+    tracker:Hide()
+    tracker.wasEmpty = true
+  end
+
+  return tracker.numWatched, tracker.numAll
+end
+
+--- Updates the selected block frame to display the given info batch
+-- If `previousBlock` is set, it will attempt to anchor to that
+-- @param blockNum the ordered block to be updated, not a watchIndex value
+-- @param info the reference returned by the GetXInfo functions
+-- REMEMBER: t.info and questData[questID] are the same table
+Default.UpdateBlock = function (handler, blockIndex, info)
+  local print = bprint
+  print('  Read list item |cFF00FFFF'..blockIndex..'|r')
+  if not blockIndex or not info then
+    return
+  end
+  local frame = handler.frame
+  local block = T.GetBlock(handler, blockIndex)
+  block.handler = handler
+  block.info = info
+  block.mainStyle = info.mainStyle or 'Normal'
+  block.subStyle = info.subStyle
+
+  info.blockIndex = blockIndex
+  if info.questID then handler.QuestBlock[info.questID] = block end
+  if info.questLogIndex then handler.LogBlock[info.questLogIndex] = block end
+  if info.watchIndex then handler.WatchBlock[info.watchIndex] = block end
+  handler.BlockInfo[blockIndex] = info
+
+  block.endPoint = block.titlebg
+  block.attachmentHeight = 0
+  handler:UpdateObjectives(block)
+
+  block.title:SetText(info.title)
+  local titleHeight = floor(block.title:GetHeight()+.5)
+  local statusHeight = floor(block.status:GetHeight()+.5)
+  local attachmentHeight =floor(block.attachmentHeight + .5)
+  local titlebgHeight = titleHeight + titleSpacing*2
+  local statusbgHeight = statusHeight + textSpacing*2
+
+  block.titlebg:SetHeight(titlebgHeight)
+
+
+  print('    |cFF0088FFanchor to', handler.currentAnchor:GetName())
+
+  print('    |cFF00FF00total sizes:')
+  print('       attachment:', attachmentHeight)
+  print('            title:', titlebgHeight, '('.. titleHeight..')')
+  --block.titlebg:SetHeight(block.title:GetHeight() + T.Conf.Wrapper.TitleSpacing)
+  block.height = titlebgHeight + attachmentHeight
+  if statusHeight > 1 then
+    block.height = block.height + statusbgHeight
+    print('           status:', statusbgHeight, '('.. statusHeight..')')
+  else
+    print('      |cFFFF0088             skipped')
+  end
+  block:SetHeight(block.height)
+
+  print('           |cFFFFFF00height|r:', block.height)
+  print('  |cFF00FFFF)|r -> ', block, block:GetHeight())
+
+  block:Show()
+
+  if info.specialItem and not info.itemButton then
+    print('    - |cFF00FFFFgenerating item button for info set')
+    info.itemButton = T.SetItemButton(block, info)
+  else
+    --info.itemButton = nil
+  end
+
+  local tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', block, 'TOPRIGHT'
+
+  if info.rewardInfo then
+    print('has immediate reward')
+    if info.rewardInfo[1].type == 'currency' or info.rewardInfo[1].type == 'item' then
+      block.icon:Show()
+      block.iconLabel:SetText(info.rewardInfo[1].count)
+      block.icon:SetPoint(tagPoint, tagAnchor, tagRelative, -2, -2)
+      tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', block.icon, 'TOPLEFT'
+      block.icon:SetTexture(info.rewardInfo[1].texture)
+    end
+  else
+    block.icon:Hide()
+  end
+
+  if info.selected then
+    block.SelectionOverlay:Show()
+  else
+    block.SelectionOverlay:Hide()
+  end
+  -- workaround for scrollchild issue where layers fall out of sync: in this case, it's by 1 vertical pixel
+  --block.highlight:SetPoint('TOPLEFT', block, 'TOPLEFT', 0, 1)
+  --block.lowlight:SetPoint('BOTTOMLEFT', block, 'BOTTOMLEFT', 0, 1)
+
+  tagPoint, tagAnchor, tagRelative = T.AddTag(block, 'frequencyTag', tagPoint, tagAnchor, tagRelative)
+  tagPoint, tagAnchor, tagRelative = T.AddTag(block, 'typeTag', tagPoint, tagAnchor, tagRelative)
+  tagPoint, tagAnchor, tagRelative = T.AddTag(block, 'completionTag', tagPoint, tagAnchor, tagRelative)
+
+  return block
+end
+
+
+
+
+--- Does the main iterations for populating block content.
+-- Hooked by corresponding handler functions where additional details need to be sorted.
+Default.UpdateObjectives = function(handler, block)
+  local print = lprint
+  local info = block.info
+  print('   |cFF00FF00default objectives routine', block:GetName())
+
+  -- reset the starting positions
+  block.attachmentHeight = 0
+  block.endPoint = block.titlebg
+  local completionScore, completionMax = 0, 0
+  local displayObjectiveHeader = false
+
+
+  --- The first line is going to be used no matter what, so it is hard-pulled.
+  -- It also ensures that we're in the right position for cleaning up the leftover lines.
+  local lineIndex = 1
+  local line = T.GetLine(handler, block, lineIndex)
+
+  block.numLines = 0
+  block.attachmentHeight = 0
+
+  if info.description and #info.description >= 1 then
+    print('   |cFF00FFFF  header line:|r', info.description)
+    line.status:SetText(info.description)
+    line.height = floor(line.status:GetStringHeight()+.5) + textSpacing
+    if line.widget then
+      line.widget:Hide()
+    end
+    T.AddLine(block, line)
+
+    lineIndex = lineIndex + 1
+    line = T.GetLine(handler, block, lineIndex)
+  end
+
+  if (info.isComplete or info.numObjectives == 0) and info.completionText then
+    print('     overriding line #1 for completion text:', info.completionText)
+    line.status:SetText(info.completionText)
+    line.height = floor(line.status:GetStringHeight()+.5) + textSpacing
+    if line.widget then
+      line.widget:Hide()
+    end
+    T.AddLine(block, line)
+  else
+    if info.objectives then
+      for i, data in ipairs(info.objectives) do
+        print('     |cFF88FF00#', i, data.type, data.text)
+        displayObjectiveHeader = true
+        line.height = 0
+        handler:UpdateLine(block, line, data)
+
+        -- For progressbar and timer lines, status text may be used as the title heading
+        if line.widget then
+          local widgetPosition = 0
+          --- WIDGET POSITION -------------------------------------------------
+          line.widget:SetPoint('TOP', line, 'TOP', 0, -widgetPosition)
+          line.widget:Show()
+          line.height = line.widget:GetHeight() + textSpacing
+          ---------------------------------------------------------------------
+          if line.displayText and #line.displayText >= 1 then
+            widgetPosition = line.status:GetHeight() + textSpacing
+            line.status:SetText(line.displayText)
+            line.height = floor(line.status:GetStringHeight()+.5) + textSpacing + line.widget.height
+            print('      - progressbar has text, adjust')
+          end
+        elseif line.displayText then
+          line.status:SetText(line.displayText)
+          line.height = floor(line.status:GetStringHeight()+.5)
+        end
+
+        T.AddLine(block, line)
+
+        --print('      sz', line:GetWidth(), line:GetHeight(), 'pt', line:GetPoint(1))
+        --print('     |cFF44BBFF#', i, 'anchoring line, size:', line.height, 'current endpoint:', line.statusbg)
+
+        lineIndex = lineIndex + 1
+        line = T.GetLine(handler, block, lineIndex)
+      end
+    end
+  end
+
+  while (block.lines[lineIndex+1]) do
+    print('      - hide |cFFFF0088'..lineIndex..'|r')
+    block.lines[lineIndex]:Hide()
+    lineIndex = lineIndex +1
+  end
+
+
+
+  if lineIndex > 0 then
+    block.attachmentHeight = block.attachmentHeight + textSpacing * 2
+    print('     |cFF00FF00attachment:', block.attachmentHeight)
+  end
+
+
+  --[[
+  local lines = handler.lines[block.index]
+  if lines and #lines > block.numLines then
+    print('   |cFFFF008' .. (#lines - block.numLines) .. ' extra lines to hide.')
+    for  i = block.numLines + 1, #lines do
+      print('    hide', i, lines[i]:GetName())
+      lines[i]:Hide()
+    end
+  end
+  ]]
+
+
+  if debug then
+    for i, region in ipairs(block.debug) do
+      for j = 1, region:GetNumPoints() do
+        local _, target = region:GetPoint(j)
+        if target:IsVisible() then
+          region:Hide()
+        else
+          region:Show()
+        end
+      end
+    end
+  end
+
+  block.completionScore = completionScore / completionMax
+end
+
+
+--- Module-specific display variables
+-- * height   - height of whatever display widget is involved in conveying the task
+-- * money    - boolean that determines listening for money events or not
+-- * progress - number ranging 0 to 2 indicating none/partial/full completion respectively
+
+
+Default.UpdateLine = function(block, line)
+  if line.finished then
+    line.progress = 2
+  elseif line.quantity > 0 then
+    line.progress  = 1
+  else
+    line.progress = 0
+  end
+  return line
+end
+----------
+--- Top level methods
+
+--- Queue any active item buttons for update for that frame
+local iprint = B.print('ItemButton')
+T.UpdateActionButtons = function(updateReason)
+  local print = iprint
+  Scroller.snap_upper = 0
+  Scroller.snap_lower = 0
+  local print = B.print('ItemButton')
+  if updateReason then
+    print = B.print('IB_'..updateReason)
+  end
+
+  local previousItem
+  for questID, itemButton in pairs(Quest.itemButtons) do
+    local info= T.Quest.Info[questID]
+
+    print('|cFF00FFFF'.. questID .. '|r', itemButton:GetName())
+    local block = T.Quest.QuestBlock[questID]
+    if block then
+        -- Dispatch the probe
+      if IsQuestWatched(info.questLogIndex) then
+        itemButton.previousItem = previousItem
+        print('  |cFFFFFF00probing', block:GetName())
+        block:SetScript('OnUpdate', function()
+          if block:GetBottom() and not InCombatLockdown() then
+            print('  '..block:GetName()..' |cFF00FF00probe hit!')
+            T.UpdateBlockAction(block, itemButton, itemButton.previousItem) -- needs to be previousItem from this scope
+            block:SetScript('OnUpdate', nil)
+
+          end
+        end)
+        previousItem = itemButton
+      else
+        print('hidden block or unwatched quest')
+        itemButton.previousItem = nil
+        itemButton:Hide()
+      end
+    elseif itemButton:IsVisible() then
+      print('  |cFFFF0088hiding unwatched quest button', itemButton:GetName())
+      itemButton.previousItem = nil
+      itemButton:Hide()
+    else
+      print('  |cFFBBBBBBignoring hidden log quest button', itemButton:GetName())
+    end
+  end
+end
+
+T.UpdateBlockAction = function (block, itemButton)
+  local print = iprint
+  print('**|cFF0088FF'..itemButton:GetName(), '|r:Update()')
+  if itemButton.questID ~= block.info.questID then
+    print('** |cFFFF0088mismatched block assignment', itemButton.questID,'<~>', block.info.questID)
+    -- something happened between this and last frame, go back and set new probes
+    return T.UpdateActionButtons()
+  end
+
+  local previousItem = itemButton.previousItem
+  local upper_bound = Scroller:GetTop() + Scroller.snap_upper
+  local lower_bound = Scroller:GetBottom() + Scroller.snap_lower + itemButtonSize
+  local point, anchor, relative
+
+  if block:GetBottom() < lower_bound then
+    print('** ',block:GetName() ,'|cFFFFFF00bottom =', floor(block:GetBottom()+.5), 'threschold =', floor(lower_bound+.5))
+    if previousItem then
+      print('adjusting', previousItem:GetName())
+      previousItem:ClearAllPoints()
+      previousItem:SetPoint('BOTTOM', itemButton, 'TOP', 0, itemButtonSpacing)
+    end
+    itemButton:ClearAllPoints()
+    itemButton.x = Wrapper:GetLeft() -4
+    itemButton.y = Wrapper:GetBottom()
+    point, anchor, relative = 'BOTTOMRIGHT', UIParent, 'BOTTOMLEFT'
+    Scroller.snap_lower = Scroller.snap_lower + itemButtonSize + itemButtonSpacing
+
+  elseif block:GetTop() > upper_bound then
+    print('** ',block:GetName() ,'|cFFFFFF00top =', floor(block:GetTop()+.5), 'threschold =', floor(upper_bound+.5))
+    itemButton:ClearAllPoints()
+    if previousItem then
+      print('latch onto another piece')
+      point, anchor, relative ='TOP', previousItem, 'BOTTOM'
+      itemButton.x = 0
+      itemButton.y = -itemButtonSpacing
+    else
+      print('latch at corner', Scroller:GetLeft() -itemButtonSpacing, Scroller:GetTop())
+      point, anchor, relative = 'TOPRIGHT', UIParent, 'BOTTOMLEFT'
+      itemButton.x = Scroller:GetLeft() -4
+      itemButton.y = Scroller:GetTop()
+    end
+    itemButton:Show()
+    Scroller.snap_upper = Scroller.snap_upper - (itemButtonSize + itemButtonSpacing)
+  else
+    print('** ',block:GetName() ,'|cFF00FF00span =', floor(block:GetBottom()+.5), floor(block:GetTop()+.5), 'threschold =', floor(lower_bound+.5))
+    itemButton:ClearAllPoints()
+    itemButton.x = block:GetLeft() - itemButtonSpacing
+    itemButton.y = block:GetTop()
+    point, anchor, relative = 'TOPRIGHT', UIParent, 'BOTTOMLEFT'
+  end
+
+  itemButton:SetPoint(point, anchor, relative, itemButton.x, itemButton.y)
+  itemButton:Show()
+end
+
+T.UpdateItemButtonCooldown = function(button)
+
+end
+
+Default.Select = function(self)
+  T:Update(self.watchReasonModule)
+end
+Default.Open = function(self)
+  T:Update(self.watchReasonModule)
+end
+Default.Remove = function(self)
+  T:Update(self.watchReasonModule)
+end
+Default.Report = function(self)
+  print('Stats:', self.numWatched,'items tracked,', self.numBlocks,'blocks assigned.')
+end
+
+Default.OnMouseUp = function(self, button)
+
+  print(self.handler.name, self.mainStyle, self.subStyle)
+  if button == 'LeftButton' then
+    if IsModifiedClick("CHATLINK") and ChatEdit_GetActiveWindow() then
+      self:Link()
+    elseif IsModifiedClick("QUESTWATCHTOGGLE") then
+      self:Remove()
+    else
+      self:Select()
+    end
+  elseif button == 'RightButton' then
+    self:Open()
+  end
+  self.initialButton = nil
+  self.modChatLink = nil
+  self.modQuestWatch = nil
+  mod:Update(self.handler.updateReasonModule)
+  print('|cFFFF8800'..tostring(self:GetName())..':MouseUp()|r')
+end
+
+Default.OnMouseDown = function(self, button)
+
+  print(IsModifiedClick("CHATLINK"), IsModifiedClick("QUESTWATCHTOGGLE"))
+  print(self.info.title)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/ObjectiveTracker.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,507 @@
+--- ${PACKAGE_NAME}
+-- @file-author@
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 3/26/2016 1:51 AM
+local B, _G = select(2,...).frame, _G
+local pairs, setmetatable, type, tostring, band, format = _G.pairs, _G.setmetatable, _G.type, _G.tostring, bit.band, string.format
+local ipairs, tinsert, hooksecurefunc = _G.ipairs, _G.tinsert, _G.hooksecurefunc
+local PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText = PlaySoundFile, IsQuestTask, SortQuestWatches, GetCurrentMapAreaID, GetZoneText, GetMinimapZoneText
+local AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS = AddQuestWatch, SetSuperTrackedQuestID, GetNumQuestWatches, AUTO_QUEST_WATCH, MAX_WATCHABLE_QUESTS
+local QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone = QuestPOIUpdateIcons, GetCVar, IsPlayerInMicroDungeon, WorldMapFrame, GetCVarBool, SetMapToCurrentZone
+local AddAutoQuestPopUp = AddAutoQuestPopUp
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('Objectives')
+local ObjectiveTrackerFrame, VeneerObjectiveScroll, CreateFrame = _G.ObjectiveTrackerFrame, _G.VeneerObjectiveScroll, _G.CreateFrame
+local Wrapper = _G.VeneerObjectiveWrapper
+local Scroller = VeneerObjectiveWrapper.scrollArea
+local Scroll = _G.VeneerObjectiveScroll
+local unitLevel = UnitLevel('player')
+
+--- Performance values
+--[[
+  self:RegisterEvent("QUEST_LOG_UPDATE");
+	self:RegisterEvent("TRACKED_ACHIEVEMENT_LIST_CHANGED");
+	self:RegisterEvent("QUEST_WATCH_LIST_CHANGED");
+	self:RegisterEvent("QUEST_AUTOCOMPLETE");
+	self:RegisterEvent("QUEST_ACCEPTED");
+	self:RegisterEvent("SUPER_TRACKED_QUEST_CHANGED");
+	self:RegisterEvent("SCENARIO_UPDATE");
+	self:RegisterEvent("SCENARIO_CRITERIA_UPDATE");
+	self:RegisterEvent("TRACKED_ACHIEVEMENT_UPDATE");
+	self:RegisterEvent("ZONE_CHANGED_NEW_AREA");
+	self:RegisterEvent("ZONE_CHANGED");
+	self:RegisterEvent("QUEST_POI_UPDATE");
+	self:RegisterEvent("VARIABLES_LOADED");
+	self:RegisterEvent("QUEST_TURNED_IN");
+	self:RegisterEvent("PLAYER_MONEY");
+ ]]
+
+
+--- These are the bitfields used by Blizzard_ObjectiveTracker to determine which segments get parsed.
+--- They are replicated here so that plugins can make use of any securehook args involving this info.
+local OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST                       -- 0x0100
+local OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP = OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP	-- 0x0200
+local OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE		-- 0x0400
+local OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO	    		        -- 0x0800
+local OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT	= OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT           -- 0x1000
+
+
+local OBJECTIVE_TRACKER_UPDATE_STATIC = OBJECTIVE_TRACKER_UPDATE_STATIC					                         	-- 0x0000
+local OBJECTIVE_TRACKER_UPDATE_ALL = OBJECTIVE_TRACKER_UPDATE_ALL			                    	          		-- 0xFFFF
+local OBJECTIVE_TRACKER_UPDATE_ID = OBJECTIVE_TRACKER_UPDATE_ID                                           -- 0
+
+local OBJECTIVE_TRACKER_UPDATE_QUEST = OBJECTIVE_TRACKER_UPDATE_QUEST				        	                  	-- 0x0001
+local OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED = OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED		               		    -- 0x0002
+local OBJECTIVE_TRACKER_UPDATE_TASK_ADDED = OBJECTIVE_TRACKER_UPDATE_TASK_ADDED		    	              		-- 0x0004
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO = OBJECTIVE_TRACKER_UPDATE_SCENARIO			      	                 	-- 0x0008
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE = OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE		       	-- 0x0010
+local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT			                   	-- 0x0020
+local OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED		  	      -- 0x0040
+local OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED = OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED		-- 0x0080
+
+local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_ALL -- default
+--- Used to determine which trackers are listening for money events
+
+T.strings = {}
+T.strings.CLICK_TO_ACCCEPT = 'Click to Accept'
+T.strings.CLICK_TO_COMPLETE = 'Click to complete'
+T.colors ={
+  enable = true,
+  default = {
+    titlebg = {'HORIZONTAL', 1, 0, .7, .25,    1, 0, .7, .125},
+    textbg = {'HORIZONTAL', 0, 0, 0, 0.4,      0, 0, 0, 0 },
+    selectionbg = {'HORIZONTAL', 1, 1, 1, 0, 1, 1, 1, 0.225},
+  },
+  daily = {
+    titlebg = {'HORIZONTAL', 0, .7, 1, .25,  0, 1, .7, .125},
+    textbg = {'HORIZONTAL', 0, .7, 1, .1,    0, 1, .7, .075 },
+  },
+  weekly = {
+    titlebg = {'HORIZONTAL', 0, .35, .7, .25,   0, .35, .7, .125},
+    textbg = {'HORIZONTAL', 0, .35, .7, .1,     0, .35, .7, .075 },
+  },
+  account = {
+    titlebg = {'HORIZONTAL', .1, .1, .1, .25, .1, .1, .1, .125},
+    textbg = {'HORIZONTAL', .1, .1, .1, 0.4, .1, .1, .1, .085 },
+  },
+  -- alliance
+  faction_1 = {
+    titlebg = {'HORIZONTAL', .2, .4, 1, 0.4, .2, .4, 1, .085 },
+    textbg = {'HORIZONTAL', .2, .4, 1, 0.4, .2, .4, 1, .085 },
+  },
+  -- horde
+  faction_2 = {
+    titlebg = {'HORIZONTAL', .6, 0, 0.4, 0.4,  .6, 0, 0.4, .085 },
+    textbg = {'HORIZONTAL', .6, 0, 0.4, 0.4,   .6, 0, 0.4, .085 },
+  }
+}
+
+T.watchMoneyReasons = 0
+
+--- Baseline defaults table; values defined in the files that they pertain to
+T.defaults = {}
+
+--- Tracker display order
+T.orderedNames = {'Bonus', 'AutoQuest', 'Quest', 'Cheevs'}
+
+--- ipairs() argument tables
+T.orderedHandlers = setmetatable({}, {__mode = "k"})
+T.orderedTrackers = setmetatable({}, {__mode = "k"})
+T.indexedTrackers = setmetatable({}, {__mode = "k"})
+
+--- pairs() argument tables
+T.namedTrackers = setmetatable({}, {__mode = "k"})
+
+local WRAPPER_ANCHOR_POINT = 'TOPRIGHT'
+local WRAPPER_OFFSET_X = 0
+local WRAPPER_OFFSET_Y = -200
+local WRAPPER_MAX_HEIGHT = 670
+local WRAPPER_WIDTH = 280
+local WRAPPER_HEADER_HEIGHT = 24
+
+T.defaults.Wrapper = {
+  AnchorPoint = WRAPPER_ANCHOR_POINT,
+  OffsetX = WRAPPER_OFFSET_X,
+  OffsetY = WRAPPER_OFFSET_Y,
+  Height = WRAPPER_MAX_HEIGHT,
+  Width = WRAPPER_WIDTH,
+  HeaderHeight = WRAPPER_HEADER_HEIGHT,
+  TextSpacing = 3,
+  TitleSpacing = 3,
+}
+
+
+
+--- Tracker module definitions begin here; innards dealing with data retreival and output are defined further in
+T.DefaultHandler = {
+  previousHeight = 0,
+
+  name = "temp",
+  displayName = "temp",
+  updateReasonModule = 0xFF00,
+  updateReasonEvent  = 0x00FF,
+
+  numWatched = 0,   --- number of entries being handled
+  numBlocks = 0,    --- number of blocks created
+  actualBlocks = 0, --- number of blocks in use
+
+  freeBlocks = {},  --- block heap
+  usedBlocks = {},
+
+  Info = {},        -- find data by ID
+  BlockInfo = {},   -- find data by block ID
+  Watched = {},     -- find watchIndex by data ID
+  WatchInfo = {},   -- find data by watch index
+  WatchBlock = {},  -- find block by watch index
+}
+
+T.AutoQuest = {
+  name = "AutoQuest",
+  displayName = "Notice",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
+}
+T.Quest = {
+  name = "Quest",
+  displayName = "Quests",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED,
+  itemButtons = {},
+  freeButtons = {},
+}
+T.Cheevs = {
+  name = "Cheevs",
+  displayName = "Achievements",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_ACHIEVEMENT,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT +
+      OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED,
+}
+T.Bonus = {
+  name = "Bonus",
+  displayName = "Bonus Objectives",
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_QUEST + OBJECTIVE_TRACKER_UPDATE_TASK_ADDED
+}
+
+T.Scenario = {
+  name = 'Scenario',
+  displayName = 'Scenario Objectives',
+  updateReasonModule = OBJECTIVE_TRACKER_UPDATE_MODULE_SCENARIO,
+  updateReasonEvents = OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE + OBJECTIVE_TRACKER_UPDATE_SCENARIO_BONUS_DELAYED
+}
+
+local Tracker_string = function (self)
+  return self.name
+end
+local Tracker_call = function (self, reason)
+  self:Update(reason)
+end
+local Handler_Initialize = function (self, name, index)
+  local c = T.Conf.Wrapper
+  print('Initializing |cFF00FFFF'..name..'|r module...')
+
+  local handler = setmetatable(T[name] or {}, {
+    __tostring = Tracker_string,
+    __call = Tracker_call
+  })
+  if type(T.orderedHandlers[index]) == 'table' then
+    return T.orderedHandlers[index]
+  end
+
+  print('|cFFFFFF00Acquiring locals')
+  local preset = {}
+  for k, _ in pairs(handler) do
+    preset[k] = true
+  end
+
+
+  print('|cFFFF8800Inheriting')
+  for k, v in pairs(self) do
+    if not handler[k] then
+      if type(v) == 'table' then
+        -- assume all tables to be local data; don't inherit or ref
+        handler[k] = {}
+      else
+        handler[k] = self[k]
+      end
+      print('copying', k)
+    end
+  end
+  print('|cFFFF4400'..tostring(name)..'|r:')
+  for k, v in pairs(handler) do
+    print(format("%32s %8s %s", (preset[k] and '|cFFFFFFFF' or '|cFFFFFF00') .. k .. '|r', type(v), tostring(v)))
+  end
+
+  T[name] = handler
+
+  local trackerName = 'Veneer'..name..'Tracker'
+  local handler = T[name]
+  local frame = CreateFrame('Frame', trackerName, _G.VeneerObjectiveScroll, 'VeneerTrackerTemplate')
+  frame.title:SetText(handler.displayName)
+  frame:SetWidth(c.Width)
+  handler.frame = frame
+  handler.trackerName = trackerName
+  handler.lines = {}
+  T.orderedTrackers[index] = frame
+  T.namedTrackers[name] = frame
+  T.indexedTrackers[handler] = frame
+  print('|cFFFF0088' .. trackerName .. '|r created for |cFF00FFFF' .. handler.displayName .. '|r module')
+
+  T.orderedHandlers[index] = handler
+  return true
+end
+
+local Event = {}
+Event.QUEST_LOG_UPDATE =  function()
+  return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE
+end
+Event.QUEST_ACCEPTED = function(questLogIndex, questID)
+  if ( IsQuestTask(questID) ) then
+    return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED, questID
+  else
+    if ( AUTO_QUEST_WATCH == "1" and GetNumQuestWatches() < MAX_WATCHABLE_QUESTS ) then
+      AddQuestWatch(questLogIndex);
+      SetSuperTrackedQuestID(questID);
+    end
+    return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST
+  end
+end
+
+Event.QUEST_WATCH_LIST_CHANGED = function(questID, added)
+  if ( added ) then
+    if ( not IsQuestTask(questID) ) then
+      return OBJECTIVE_TRACKER_UPDATE_QUEST_ADDED, questID, added
+    end
+  else
+    return OBJECTIVE_TRACKER_UPDATE_QUEST, questID, added
+  end
+end
+
+Event.QUEST_POI_UPDATE = function()
+  QuestPOIUpdateIcons();
+  if ( GetCVar("trackQuestSorting") == "proximity" ) then
+    SortQuestWatches();
+  end
+  return OBJECTIVE_TRACKER_UPDATE_ALL
+end
+Event.SUPER_TRACKED_QUEST_CHANGED = function()
+  return OBJECTIVE_TRACKER_UPDATE_QUEST
+end
+Event.ZONE_CHANGED = function()
+
+  local inMicroDungeon = IsPlayerInMicroDungeon();
+  if ( inMicroDungeon ~= T.inMicroDungeon ) then
+    if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
+      SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
+    end
+    --SortQuestWatches();
+    T.inMicroDungeon = inMicroDungeon;
+  end
+end
+Event.QUEST_AUTOCOMPLETE = function(questId)
+  AddAutoQuestPopUp(questId, "COMPLETE");
+  return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST + OBJECTIVE_TRACKER_UPDATE_MODULE_AUTO_QUEST_POPUP
+end
+Event.SCENARIO_CRITERIA_UPDATE =  function()
+  return OBJECTIVE_TRACKER_UPDATE_SCENARIO
+end
+Event.SCENARIO_UPDATE = function(newStage)
+  if ( newStage ) then
+    return OBJECTIVE_TRACKER_UPDATE_SCENARIO_NEW_STAGE
+  else
+    return OBJECTIVE_TRACKER_UPDATE_SCENARIO
+  end
+end
+Event.TRACKED_ACHIEVEMENT_UPDATE = function()
+  return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT
+end
+Event.TRACKED_ACHIEVEMENT_LIST_CHANGED = function(achievementID, added)
+  if ( added ) then
+    return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT_ADDED, achievementID
+  else
+    return OBJECTIVE_TRACKER_UPDATE_ACHIEVEMENT
+  end
+end
+Event.ZONE_CHANGED_NEW_AREA = function ()
+  if ( not WorldMapFrame:IsShown() and GetCVarBool("questPOI") ) then
+    SetMapToCurrentZone();			-- update the zone to get the right POI numbers for the tracker
+  end
+  SortQuestWatches();
+  T.currentZoneArea = GetCurrentMapAreaID()
+  print('Updating zone ID to', T.currentZoneArea, '=', GetZoneText(), GetMinimapZoneText())
+
+
+  return OBJECTIVE_TRACKER_UPDATE_TASK_ADDED
+end
+
+
+Event.PLAYER_MONEY = function()
+  if T.watchMoneyReasons > 0 then
+    return T.watchMoneyReasons
+  end
+end
+Event.CRITERIA_COMPLETE = function()
+  return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE
+end
+Event.QUEST_TURN_IN = function(questID, xp, money)
+  if ( IsQuestTask(questID) ) then
+    T.Bonus:OnTurnIn(questID, xp, money)
+    print('updating bonus modules (code', OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, ',', questID, xp, money)
+    return OBJECTIVE_TRACKER_UPDATE_MODULE_BONUS_OBJECTIVE, questID, xp, money
+  else
+    return OBJECTIVE_TRACKER_UPDATE_MODULE_QUEST, questID, xp, money
+  end
+end
+T.Event = Event
+
+--- Done once per ui load
+local BlizzHooks = {
+  ['AddQuestWatch'] = 'AddQuestWatch',
+  ['RemoveQuestWatch'] = 'RemoveQuestWatch',
+  ['AbandonQuest'] = 'AbandonQuest',
+  ['AcknowledgeAutoAcceptQuest'] = 'AcknowledgeAutoAcceptQuest',
+  ['AddAutoQuestPopUp'] = 'AddAutoQuestPopUp',
+  ['RemoveAutoQuestPopUp'] = 'RemoveAutoQuestPopUp',
+  ['AddTrackedAchievement'] = 'AddTrackedAchievement',
+  ['RemoveTrackedAchievement'] = 'RemoveTrackedAchievement',
+  ['SetSuperTrackedQuestID'] = 'SetSuperTrackedQuestID'
+}
+local VeneerData
+
+T.SetWatchMoney = function(watchMoney, reason)
+  if watchMoney then
+    if band(T.watchMoneyReasons, reason) == 0 then
+      T.watchMoneyReasons = T.watchMoneyReasons + reason;
+    end
+  else
+    if band(T.watchMoneyReasons, reason) > 0 then
+      T.watchMoneyReasons = T.watchMoneyReasons - reason;
+    end
+  end
+end
+T.animateReasons = 0
+T.SetAnimate = function(reason)
+  print('comparing', T.animateReasons, reason)
+  if animate then
+    if band(T.animateReasons, reason) == 0 then
+      T.animateReasons = T.animateReasons + reason
+    end
+  else
+    if band(T.animateReasons, reason) > 0 then
+      T.animateReasons = T.animateReasons - reason
+    end
+  end
+end
+
+local Play = function(file) if Devian and Devian.InWorkspace() then PlaySoundFile(file) end end
+
+function T:OnEvent (event, ...)
+  local isHandled
+  print('OnEvent(|cFF00FF00'.. event ..'|r):', ...)
+  local reason, arg1, arg2, arg3
+  if Event[event] then
+    if type(Event[event]) == 'function' then
+      Play([[Interface\Addons\SharedMedia_MyMedia\sound\Info.ogg]])
+      reason, arg1, arg2, arg3 = Event[event](...)
+    elseif type(Event[event]) == 'table' then
+      Play([[Interface\Addons\SharedMedia_MyMedia\sound\Link.ogg]])
+      for i, action in ipairs(Event[event]) do
+        if type(action) == 'function' then
+          reason, arg1, arg2, arg3 = action(event, ...)
+        else
+          reason = action
+        end
+
+        if reason then
+          T:Update(reason, arg1, arg2, arg3)
+        end
+      end
+    else
+      Play([[Interface\Addons\SharedMedia_MyMedia\sound\Heart.ogg]])
+      reason = Event[event]
+    end
+  else
+    Play([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]])
+  end
+  if reason then
+    T:Update(reason, arg1, arg2, arg3)
+  else
+    print('no reason value returned')
+    Play([[Interface\Addons\SharedMedia_MyMedia\sound\Quack.ogg]])
+  end
+
+end
+
+
+function T:OnInitialize()
+  local c = T.Conf.Wrapper
+  VeneerData = _G.VeneerData
+  VeneerData.CallLog = VeneerData.CallLog or {}
+  if not T.isHooked then
+    T.isHooked = true
+    for blizzFunc, veneerFunc in pairs(BlizzHooks) do
+      if T[veneerFunc] then
+        hooksecurefunc(blizzFunc, T[veneerFunc])
+      else
+        hooksecurefunc(blizzFunc, function(...)
+          print('|cFFFF0088securehook('..tostring(blizzFunc)..')|r args:', ...)
+          tinsert(VeneerData.CallLog, {blizzFunc, ...})
+        end)
+      end
+    end
+  end
+
+  T.Conf.TasksLog = T.Conf.TasksLog or {}
+
+  ObjectiveTrackerFrame:UnregisterAllEvents()
+  ObjectiveTrackerFrame:Hide()
+
+
+  for id, name in ipairs(T.orderedNames) do
+    if not T.orderedHandlers[id] then
+      Handler_Initialize(T.DefaultHandler, name, id)
+    end
+  end
+  self:SetSize(c.Width, 40)
+  T.InitializeWidgets()
+end
+
+--- Done any time the the minimize button is toggled up
+function T:OnEnable()
+
+  print(B.Conf.VeneerObjectiveWrapper.enabled)
+  if not B.Conf.VeneerObjectiveWrapper.enabled then
+    return
+  end
+
+  for event, action in pairs(Event) do
+    print('|cFFFF0088listen to', event, 'for action|r', tostring(action))
+    Wrapper:RegisterEvent(event)
+  end
+
+  local c = T.Conf.Wrapper
+
+  Scroller:SetScrollChild(Scroll)
+  Scroller:SetWidth(c.Width)
+  Scroll:SetWidth(c.Width)
+  Scroll:ClearAllPoints()
+  Scroll:SetPoint('TOP', Scroller, 'TOP')
+  self:SetScript('OnEvent', T.OnEvent)
+
+  Scroller:Show()
+
+  local from, target, to, x, y = Wrapper:GetPoint(1)
+  print(from, target:GetName(), to, x,y)
+
+  T:Update()
+
+  -- run once to prime the data structure
+  T.UpdateActionButtons()
+end
+
+function T:OnDisable()
+  self:UnregisterAllEvents()
+  Scroller:Hide()
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Quests.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,351 @@
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
+local GetAutoQuestPopUp, GetQuestLogCompletionText = GetAutoQuestPopUp, GetQuestLogCompletionText
+local Default, Quest = T.DefaultHandler, T.Quest
+local format = format
+local print = B.print('Tracker')
+local lprint = B.print('Line')
+local iprint = B.print('Info')
+
+local colors = T.colors
+
+local tprint = B.print('Tracker')
+Quest.Update = function(self, reason, ...)
+  local print = tprint
+  print('QuestTracker:Update() received')
+  T.UpdateActionButtons()
+  Default.Update(self, reason, ...)
+end
+
+T.Quest.numButtons = 0
+local usedButtons = T.Quest.itemButtons
+local freeButtons = T.Quest.freeButtons
+
+Quest.UpdateObjectives = function(handler, block)
+  local print = lprint
+  print('|cFF00FFFFUpdateObjectives()')
+  local info = block.info
+
+  local titlebg, textbg = colors.default.titlebg, colors.default.textbg
+  print((info.isAccount and 'isAccount' or ''), (info.isFaction and 'isFaction' or ''), (info.isDaily and 'isDaily' or ''), (info.isWeekly and 'isWeekly' or ''), info.tagID, info.tagName)
+
+  if info.isAccount then
+    if info.isFaction then
+      print('     faction', info.tagID)
+      titlebg, textbg = colors['faction_'..info.tagID].titlebg, colors.default.textbg
+    else
+      print('     account', info.isAccount, info.isFaction)
+      titlebg, textbg = colors.account.titlebg, colors.account.textbg
+    end
+  elseif info.isDaily then
+    print('     daily', info.frequency)
+    titlebg, textbg = colors.daily.titlebg, colors.daily.textbg
+  elseif info.isWeekly then
+    print('     weekly', info.frequency)
+    titlebg, textbg = colors.weekly.titlebg, colors.weekly.textbg
+  end
+
+  block.titlebg:SetGradientAlpha(unpack(titlebg))
+  block.statusbg:SetGradientAlpha(unpack(textbg))
+
+  local completionText
+  if info.isComplete then
+    if info.isAutoComplete then
+      local questID, popupType = GetAutoQuestPopUp(info.questLogIndex)
+      if popupType == 'COMPLETE' then
+        print('    :: auto-complete quest :: set the message')
+        info.completionText = T.strings.CLICK_TO_COMPLETE
+      end
+    else
+      if not completionText or info.completionText then
+        info.completionText = GetQuestLogCompletionText(info.questLogIndex)
+      end
+    end
+    print('    :: complete quest :: show instruction: "'.. tostring(info.completionText) .. '"')
+  end
+
+  Default.UpdateObjectives(handler, block)
+end
+
+Quest.UpdateLine = function(handler, block, line, data)
+  local print = lprint
+  local objectiveType = data.type
+  local r, g, b, a = 0, 1, 1, 1
+
+  line.progress = 0
+  if data.finished then
+    line.progress = 2
+    r, g, b, a = 0, 1, 0, 1
+  elseif objectiveType == 'monster' then
+    r, g, b, a = 1, .55, .2, 1
+  elseif objectiveType == 'item' then
+    r, g, b, a = .8, .8, .8, 1
+  elseif objectiveType == 'object' then
+    r, g, b, a = 1, 1, 1, 1
+  elseif objectiveType == 'player' then
+    r, g, b, a = 0, 0.8, 1, 1
+  end
+  print(format('     |cFF%02X%02X%02X%0.1f, %0.1f, %0.1f|r', (r * 255), g * 255, b * 255, r, g, b))
+
+  line.displayColor = {r, g, b, a}
+  line.status:SetTextColor(r, g, b, a)
+  line.displayText = data.text
+
+  return line
+end
+
+-----------------------------
+--- QUEST
+Quest.POI = {}
+Quest.QuestBlock = {}
+Quest.LogBlock = {}
+Quest.LogInfo = {}
+
+function Quest:GetNumWatched ()
+  print(self.name, self)
+  self.numAll = GetNumQuestLogEntries()
+  self.numWatched = GetNumQuestWatches()
+  return self.numWatched, self.numAll
+end
+Quest.GetInfo = function (self, watchIndex)
+  local print = iprint
+  print('|cFF00DDFFQuest|r.|cFF0088FFGetInfo(|r'.. tostring(watchIndex)..'|r)')
+  local questID, title, questIndex, numObjectives, requiredMoney, _,
+  _, isAutoComplete, failureTime, timeElapsed, questType, _, _, _, _ = GetQuestWatchInfo(watchIndex)
+
+  if not questIndex then
+    return
+  end
+
+
+  local _, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, _, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questIndex)
+
+
+  if not questID then
+    return
+  end
+  Quest.Info[questID] = Quest.Info[questID] or {}
+
+  local q = Quest.Info[questID]
+  q.watchIndex = watchIndex
+  q.type = 'Quest'
+  q.questID = questID
+  q.title = title
+  q.level = level
+  q.displayQuestID = displayQuestID
+  q.suggestedGroup = suggestedGroup
+  q.questLogIndex = questIndex
+  q.numObjectives = numObjectives
+  q.requiredMoney = requiredMoney
+  q.isComplete = isComplete
+  q.startEvent = startEvent
+  q.isAutoComplete = isAutoComplete
+  q.failureTime = failureTime
+  q.timeElapsed = timeElapsed
+  q.questType = questType
+  q.isTask = isTask
+  q.isStory = isStory
+  q.isOnMap = isOnMap
+  q.hasLocalPOI = hasLocalPOI
+  q.frequency = frequency
+  q.isComplete = isComplete
+  q.isStory = isStory
+  q.isTask = isTask
+
+  --- resolve icon type and template
+  local questTagID, tagName = GetQuestTagInfo(questID)
+  local tagID
+
+  local factionGroup = GetQuestFactionGroup(questID);
+  if( questTagID and questTagID == QUEST_TAG_ACCOUNT ) then
+    if( factionGroup ) then
+      tagID = "ALLIANCE";
+      if ( factionGroup == LE_QUEST_FACTION_HORDE ) then
+        tagID = "HORDE";
+      end
+      q.isFaction = true
+    else
+      tagID = QUEST_TAG_ACCOUNT;
+      q.isAccount = true
+    end
+    q.typeTag = QUEST_TAG_TCOORDS[tagID]
+  elseif ( factionGroup) then
+    tagID = "ALLIANCE";
+    if ( factionGroup == LE_QUEST_FACTION_HORDE ) then
+      tagID = "HORDE";
+    end
+    q.isFaction = true
+  end
+
+  if( frequency == LE_QUEST_FREQUENCY_DAILY and (not isComplete or isComplete == 0) ) then
+    tagID = "DAILY";
+    q.frequencyTag = QUEST_TAG_TCOORDS["DAILY"]
+    q.isDaily = true
+  elseif( frequency == LE_QUEST_FREQUENCY_WEEKLY and (not isComplete or isComplete == 0) )then
+    tagID = "WEEKLY";
+    q.frequencyTag = QUEST_TAG_TCOORDS["WEEKLY"]
+    q.isWeekly = true
+  elseif( questTagID ) then
+    tagID = questTagID;
+  end
+
+  if ( isComplete and isComplete < 0 ) then
+    q.completionTag = QUEST_TAG_TCOORDS["FAILED"]
+    q.isFailed = true
+  elseif isComplete then
+    q.completionTag = QUEST_TAG_TCOORDS["COMPLETED"]
+  end
+
+
+  q.tagID = questTagID
+  q.tagName = tagName
+  --q.isBreadCrumb = isBreadCrumb
+  q.completionText= GetQuestLogCompletionText(questIndex)
+  q.numObjectives = GetNumQuestLeaderBoards(questIndex)
+  q.objectives = {}
+  for i = 1, q.numObjectives do
+    local text, type, finished = GetQuestLogLeaderBoard(i, questIndex)
+    print(format('   #%d %s %s %s', i, tostring(type), tostring(text), tostring(finished)))
+    q.objectives[i] = {
+      index = i,
+      type = type,
+      text = text,
+      finished = finished
+    }
+    if type == 'event' then
+    elseif type == 'monster' then
+    elseif type == 'object' then
+    elseif type == 'reputation' then
+    elseif type == 'item' then
+    end
+  end
+
+  if requiredMoney >= 1 then
+    local money = GetMoney()
+    local moneyText = money
+    local requiredSilver, requiredCopper
+    local requiredGold = (requiredMoney > 10000) and (floor(requiredMoney/10000)) or nil
+    if mod(requiredMoney, 10000) ~= 0 then
+      requiredSilver = (requiredMoney > 100) and (mod(requiredMoney, 10000) / 100) or nil
+      if mod(requiredMoney, 100) ~= 0 then
+        requiredCopper = mod(requiredMoney, 100)
+      end
+    end
+
+    -- round the money value down
+    if requiredMoney > 9999 and not (requiredSilver or requiredCopper) then
+      moneyText = floor(money/10000)
+    elseif requiredMoney < 10000 and mod(requiredMoney,100) == 0 then
+      moneyText = floor(money/100)
+    end
+
+    local text = moneyText
+    local index = #q.objectives + 1
+    local finished = (GetMoney() >= requiredMoney)
+
+    if not finished then
+      text = text .. ' / ' .. GetCoinTextureString(requiredMoney, 12)
+    else
+      text = '' .. GetCoinTextureString(requiredMoney, 12)
+    end
+    q.objectives[index] = {
+      index = index,
+      type = 'progressbar',
+      quantity = money,
+      requiredQuantity = requiredMoney,
+      text = text,
+      finished = finished
+    }
+    print(format('   #%d %s %s %s', index, 'money', text, tostring(finished)))
+  end
+
+
+  local link, icon, charges = GetQuestLogSpecialItemInfo(questIndex)
+  local start, duration, enable = GetQuestLogSpecialItemCooldown(questIndex)
+  if link or icon or charges then
+    q.specialItem = {
+      questID = questID,
+      questIndex = questIndex,
+      link = link,
+      charges = charges,
+      icon = icon,
+      start = start,
+      duration = duration,
+      enable = enable,
+    }
+  end
+
+  if QuestHasPOIInfo(questID) then
+    local distance, onContinent = GetDistanceSqToQuest(questIndex)
+    if distance ~= nil and distance > 0 then
+      self.POI[questIndex] = {
+        questIndex = questIndex,
+        questID = questID,
+        distance = distance,
+        onContinent = onContinent
+      }
+    end
+  end
+
+
+  q.selected =  (questID == GetSuperTrackedQuestID()) -- call directly so artifact data doesn't become an issue
+  self.WatchInfo[watchIndex] = q
+  self.LogInfo[questIndex] = q
+  print('- logIndex =', questIndex, 'title =', title)
+  for k,v in pairs(q) do
+    print('|cFFFFFF00'..k..'|r:', v)
+  end
+  return q
+end
+
+Quest.GetClosest = function()
+  local minID, minTitle
+  local minDist = math.huge
+  local numQuests = GetNumQuestLogEntries()
+  for questIndex =  1, numQuests do
+    local distance, onContinent = GetDistanceSqToQuest(questIndex)
+    local title, level, _, _, _, _, _, _, questID = GetQuestLogTitle(questIndex)
+    if onContinent and distance < minDist then
+      minDist = distance
+      minTitle = title
+      minID = questID
+    end
+  end
+
+  print('nearest quest is', minTitle, 'by', math.sqrt(minDist))
+  return minID, minTitle, minDist
+end
+
+Quest.OnTurnIn = function(self, questID, xp, money)
+
+end
+
+
+Quest.Select = function(self)
+
+  if self.info.isAutoComplete and self.info.isComplete then
+    ShowQuestComplete(self.info.questLogIndex)
+  else
+    SetSuperTrackedQuestID(self.info.questID)
+  end
+end
+
+Quest.Link = function(self)
+
+  local questLink = GetQuestLink(self.info.questLogIndex);
+  if ( questLink ) then
+    ChatEdit_InsertLink(questLink);
+  end
+end
+
+Quest.Open = function(self)
+
+  QuestMapFrame_OpenToQuestDetails(self.info.questID)
+end
+
+Quest.Remove = function(self)
+
+  print('removing', self.info.questLogIndex, 'from watcher')
+  RemoveQuestWatch(self.info.questLogIndex)
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Widgets.lua	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,440 @@
+local B = select(2,...).frame
+local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
+local print = B.print('WidgetFactory')
+local _G, UIParent = _G, UIParent
+local GetQuestLogSpecialItemInfo, IsQuestLogSpecialItemInRange, GetQuestLogSpecialItemCooldown = GetQuestLogSpecialItemInfo, IsQuestLogSpecialItemInRange, GetQuestLogSpecialItemCooldown
+local CooldownFrame_SetTimer, SetItemButtonTextureVertexColor, CreateFrame, VeneerObjectiveScroll = CooldownFrame_SetTimer, SetItemButtonTextureVertexColor, CreateFrame, VeneerObjectiveScroll
+local tremove, tContains, pairs, ipairs, setmetatable, floor = tremove, tContains, pairs, ipairs, setmetatable, floor
+local SetItemButtonTexture, SetItemButtonCount = SetItemButtonTexture, SetItemButtonCount
+local ToggleWorldMap, GetTrackedAchievements, GetTasksTable = ToggleWorldMap, GetTrackedAchievements, GetTasksTable
+-- GLOBALS: Veneer_QuestObjectiveItem_UpdateCooldown, Veneer_QuestObjectiveItem_OnUpdate
+--- frame refs
+local Wrapper = _G.VeneerObjectiveWrapper
+local Scroller = Wrapper.scrollArea
+local CloseButton = Wrapper.CloseButton
+local QuestMapButton = Wrapper.QuestMapButton
+local Scroll = _G.VeneerObjectiveScroll
+local usedButtons = T.Quest.itemButtons
+local freeButtons = T.Quest.freeButtons
+
+T.buttons = {
+  CloseButton = {
+    closedSwatch = {
+      [[Interface\Buttons\UI-Panel-QuestHideButton]],
+      [[Interface\Buttons\UI-Panel-QuestHideButton]],
+      0, 0.5, 0.5, 1,
+      0.5, 1, 0.5, 1,
+    },
+    openSwatch = {
+      [[Interface\Buttons\UI-Panel-QuestHideButton]],
+      [[Interface\Buttons\UI-Panel-QuestHideButton]],
+      0.5, 1, 0.5, 1,
+      0, 0.5, 0.5, 1,
+    },
+    parent = 'VeneerObjectiveWrapper'
+  },
+  QuestMapButton = {
+    closedSwatch = {
+      [[Interface\QUESTFRAME\UI-QUESTMAP_BUTTON]],
+      [[Interface\QUESTFRAME\UI-QUESTMAP_BUTTON]],
+      0, 1, 0.5, 1,
+      0, 1, 0, 0.5,
+    },
+    openSwatch = {
+      [[Interface\QUESTFRAME\UI-QUESTMAP_BUTTON]],
+      [[Interface\QUESTFRAME\UI-QUESTMAP_BUTTON]],
+      0, 1, 0, 0.5,
+      0, 1, 0.5, 1,
+    }
+  }
+}
+
+local Scroller_OnShow = function()
+  Wrapper.watchMoneyReasons = 0;
+  --T:Update()
+  --T:OnInitialize()
+  for i, region in ipairs(Wrapper.headerComplex) do
+    region:Show()
+  end
+end
+
+local Scroller_OnHide = function()
+  local self = Wrapper
+  Wrapper:UnregisterAllEvents()
+  Wrapper:SetScript('OnEvent', nil)
+  for i, region in ipairs(Wrapper.headerComplex) do
+    region:Hide()
+  end
+end
+
+local Scroller_OnMouseWheel = function(self, delta)
+  local r = Scroll:GetHeight() - Scroller:GetHeight()
+  local s = B.Conf.ObjectiveScroll - delta * floor(r/5+.5)
+  local from = self:GetVerticalScroll()
+  print('|cFF00FF00OnMouseWheel', 'scroll =', s)
+  if s >= r then
+    s = r
+  elseif s < 1 then
+    s = 0
+  end
+  self:SetVerticalScroll(s)
+  B.Conf.ObjectiveScroll = s
+  print('|cFF00FF00OnMouseWheel', 'from = ', from, 'scroll =', s, ' range =', r, 'current =', self:GetVerticalScroll())
+
+  T.UpdateActionButtons('SCROLLING')
+end
+
+local UpdatePanelButton = function (self, state)
+  state = state or true
+  local swatch = (state == true) and self.openSwatch or self.closedSwatch
+  print((state ~= true) and 'closedSwatch' or 'openSwatch')
+  self:SetNormalTexture(swatch[1])
+  self:SetPushedTexture(swatch[2])
+  if #swatch >= 6 then
+    self:GetNormalTexture():SetTexCoord(swatch[3], swatch[4], swatch[5], swatch[6])
+  end
+  if #swatch == 10 then
+    self:GetPushedTexture():SetTexCoord(swatch[7], swatch[8], swatch[9], swatch[10])
+  end
+
+end
+
+local OnClick = {}
+OnClick.CloseButton = function(self)
+  T:SetEnabled()
+  UpdatePanelButton(self, T.Conf.enabled)
+end
+
+OnClick.QuestMapButton = function()
+  ToggleWorldMap()
+end
+
+
+T.InitializeWidgets = function()
+  local panelButtons = T.buttons
+  --- tracker scroll
+  Scroller:SetScript('OnMouseWheel', Scroller_OnMouseWheel)
+  Scroller:SetScript('OnShow', Scroller_OnShow)
+  Scroller:SetScript('OnHide', Scroller_OnHide)
+  for name, swatch in pairs(panelButtons) do
+    local source = swatch and swatch or panelButtons.CloseButton
+    local button = Wrapper[name]
+    button.parent = swatch.parent
+    button.openSwatch = source.openSwatch
+    button.closedSwatch = source.closedSwatch
+    if OnClick[name] then
+      button:SetScript('OnClick', OnClick[name])
+    end
+    UpdatePanelButton(button, T.Conf.enabled)
+  end
+end
+
+----------------------------------------------------------------------------------------
+--- modified version of the itemButton initializer used by Barjack's 'QuestKing 2' addon,
+--- url: http://mods.curse.com/addons/wow/questking
+----------------------------------------------------------------------------------------
+T.SetItemButton = function(block, info)
+  local itemInfo = info.specialItem
+  if not itemInfo then
+    return
+  end
+
+  local itemButton
+  if not info.itemButton then
+    if #freeButtons >= 1 then
+      print('    |cFF00FFFFfound a free button')
+      itemButton = freeButtons[#freeButtons]
+      freeButtons[#freeButtons] = nil
+      if itemButton.block then
+        itemButton.block.itemButton = nil
+        itemButton.block = nil
+      end
+    else
+      local buttonIndex = T.Quest.numButtons + #freeButtons + 1
+      itemButton = CreateFrame('Button', 'VeneerQuestItemButton' .. buttonIndex, UIParent, 'VeneerItemButtonTemplate')
+      itemButton.buttonIndex = buttonIndex
+      itemButton:SetSize(36, 36)
+      itemButton:GetNormalTexture():SetSize(36 * (5/3), 36 * (5/3))
+      print('    |cFFFF4400starting new button', itemButton:GetName())
+    end
+    T.Quest.numButtons = T.Quest.numButtons + 1
+  else
+    itemButton = info.itemButton
+    print('    |cFF00FF00found assigned button', itemButton:GetName())
+
+  end
+  -- set values
+
+  info.itemButton = itemButton
+  usedButtons[info.questID] = itemButton
+  print('      |cFF8800FFassigning|r', itemButton:GetName(), 'to quest|cFF00FF00', info.questID, '|rat|cFFFFFF00', block:GetName(),'|r')
+
+  for k,v in pairs(usedButtons) do
+    print('|cFFFF44DD'..k..'|r', v:GetName())
+  end
+
+  itemButton:SetAttribute("type", "item")
+  itemButton:SetAttribute("item", itemInfo.link)
+
+  itemButton.questID = info.questID
+  itemButton.questLogIndex = info.questLogIndex
+  itemButton.charges = itemInfo.charges
+  itemButton.rangeTimer = -1
+  itemButton.block = block
+
+  SetItemButtonTexture(itemButton, itemInfo.icon)
+  SetItemButtonCount(itemButton, itemInfo.charges)
+  Veneer_QuestObjectiveItem_UpdateCooldown(itemButton);
+
+  return itemButton
+end
+--- Clear an itemButton from the given block
+T.FreeItemButtons = function(block)
+
+  if block.itemButton then
+    local itemButton = block.itemButton
+    if itemButton.questID ~= block.info.questID then
+      block.itemButton = nil
+      itemButton.block = T.Quest.InfoBlock[itemButton.questID]
+    else
+      itemButton.block = nil
+      itemButton:Hide()
+
+      usedButtons[itemButton.questID] = nil
+      freeButtons[#freeButtons + 1] = itemButton
+      T.Quest.numButtons = T.Quest.numButtons - 1
+      print('|cFFFF0088released', itemButton:GetName(),'and', block:GetName())
+    end
+  end
+end
+
+function Veneer_QuestObjectiveItem_OnUpdate (self, elapsed)
+  -- Handle range indicator
+  local rangeTimer = self.rangeTimer
+  if (rangeTimer) then
+    rangeTimer = rangeTimer - elapsed
+    if (rangeTimer <= 0) then
+      local link, item, charges, showItemWhenComplete = GetQuestLogSpecialItemInfo(self.questLogIndex)
+      if ((not charges) or (charges ~= self.charges)) then
+        T:Update()
+        return
+      end
+
+      local count = self.HotKey
+      local valid = IsQuestLogSpecialItemInRange(self.questLogIndex)
+      if (valid == 0) then
+        count:Show()
+        count:SetVertexColor(1.0, 0.1, 0.1)
+      elseif (valid == 1) then
+        count:Show()
+        count:SetVertexColor(0.6, 0.6, 0.6)
+      else
+        count:Hide()
+      end
+      rangeTimer = TOOLTIP_UPDATE_TIME
+    end
+
+    self.rangeTimer = rangeTimer
+  end
+end
+
+function Veneer_QuestObjectiveItem_UpdateCooldown (itemButton)
+  local start, duration, enable = GetQuestLogSpecialItemCooldown(itemButton.questLogIndex)
+  if (start) then
+    CooldownFrame_SetTimer(itemButton.Cooldown, start, duration, enable)
+    if (duration > 0 and enable == 0) then
+      SetItemButtonTextureVertexColor(itemButton, 0.4, 0.4, 0.4)
+    else
+      SetItemButtonTextureVertexColor(itemButton, 1, 1, 1)
+    end
+  end
+end
+
+-----------------------------------------
+-- Criteria frames
+
+--[[
+      text = description,
+      type = type,
+      finished = completed,
+      quantity = quantity,
+      requiredQuantity = requiredQuantity,
+      characterName = characterName,
+      flags = flags,
+      assetID = assetID,
+      quantityString = quantityString,
+      criteriaID = criteriaID,
+]]
+local newWidgetID = 0
+T.WidgetRegistry = {}
+local wr = T.WidgetRegistry
+
+--- Get a usable widget for the given achievement criteria set.
+-- Returns a frame object with dimensioning parameters needed to size the receiving tracker block
+T.SetWidget = function(line, data, objectiveType, objectiveKey)
+  local print = B.print('ObjectiveWidgets')
+  local widgetType = objectiveType
+  local widget
+  if wr[widgetType] and wr[widgetType].used[objectiveKey] then
+    widget = wr[widgetType].used[objectiveKey]
+    print('|cFF00FF00Updating ('..objectiveKey..')', widget)
+  elseif not wr[widgetType] or #wr[widgetType].free == 0 then
+    widget = CreateFrame('Frame', 'VeneerObjective' .. widgetType .. (wr[widgetType] and (wr[widgetType].lastn+1) or (1)), VeneerObjectiveScroll, 'VeneerObjectiveCriteria' .. widgetType)
+
+    print('|cFFFF0088Creating `'..widget:GetName()..'` id', wr[widgetType].lastn)
+  else
+    widget = tremove(wr[widgetType].free)
+    print('|cFFFFFF00Acquiring released widget', widget:GetName())
+  end
+
+
+  wr[widgetType].used[objectiveKey] = widget
+  widget.line = line
+  widget.objective = data
+  widget.key = objectiveKey
+  T.InitializeWidget(widget)
+  return widget
+end
+
+--- WidgetTemplate 'OnLoad'
+T.RegisterWidget = function(frame)
+  local print = B.print('ObjectiveWidgets')
+  local widgetType = frame.widgetType
+  if not wr[frame.widgetType] then
+    print('|cFFFF4400[[WidgetTemplate]]|r', widgetType)
+    wr[widgetType] = { lastn = 1, free = {}, used = {}, usedIndex = {}, freeIndex = {} }
+  else
+    print('|cFF0088FF+ [[WidgetTemplate]]r', widgetType, wr[widgetType].lastn)
+    wr[widgetType].lastn = wr[widgetType].lastn + 1
+  end
+end
+
+--- WidgetTemplate 'OnShow'
+T.InitializeWidget = setmetatable({}, {
+  __call = function(t, frame)
+    -- todo: config pull
+
+    frame:SetWidth(T.Conf.Wrapper.Width - T.Conf.Style.Format.status.Indent * 2)
+    frame:SetScript('OnEvent', T.UpdateWidget[frame.widgetType])
+    frame:RegisterEvent('TRACKED_ACHIEVEMENT_UPDATE')
+    frame:RegisterEvent('TRACKED_ACHIEVEMENT_LIST_CHANGED')
+    frame:RegisterEvent('CRITERIA_UPDATE')
+    frame:RegisterEvent('CRITERIA_COMPLETE')
+    frame:RegisterEvent('CRITERIA_EARNED')
+    t[frame.widgetType](frame)
+    T.UpdateWidget[frame.widgetType](frame)
+  end,
+})
+
+--- WidgetTemplate 'OnEvent'
+T.UpdateWidget = setmetatable({}, {
+  __call = function(t, frame)
+    if not frame.widgetType then
+      error('Invalid widget template, needs .widgetType')
+      return
+    end
+
+    return t[frame.widgetType](frame)
+  end
+})
+
+--- WidgetTemplate 'OnHide'
+T.ReleaseWidget = function(frame)
+  --[[
+  local print = B.print('ObjectiveWidgets')
+  local reg = wr[frame.widgetType]
+  if reg and reg.used[frame.key] then
+    reg.used[frame.key] = nil
+    frame.line = nil
+    frame.info = nil
+    frame:UnregisterAllEvents()
+    tinsert(reg.free, frame)
+    print('|cFFBBBBBBreleased from service', frame:GetName())
+  end
+  ]]
+end
+
+--- RemoveTrackedAchievement post-hook
+T.CleanWidgets = function()
+  local print = B.print('ObjectiveWidgets')
+  local tracked = {GetTrackedAchievements() }
+  local tasks = GetTasksTable()
+  for type, reg in pairs(T.WidgetRegistry) do
+    print('collecting', type)
+    for key, frame in pairs(reg.used) do
+      if frame.objective.cheevID then
+        local id = frame.objective.cheevID
+
+        if id and not tContains(tracked, id) then
+
+          print('  untracked achievement', id, 'associated with', key, frame:GetName())
+          frame:Hide()
+        end
+      elseif frame.objective.questID then
+        -- do something for quest task
+      end
+    end
+  end
+end
+
+
+
+T.defaults.WidgetStyle = {
+
+}
+
+local progressHeight = 16
+local progressBorder = 1
+local progressIndent = 3
+local progressFont = _G.VeneerCriteriaFontNormal
+
+
+T.InitializeWidget.ProgressBar = function(self)
+  local c = T.Conf.Wrapper
+  self.height = progressHeight + c.TextSpacing
+  self.width = c.Width - c.TextSpacing
+  self.indent = progressIndent
+
+  self:SetHeight(progressHeight)
+  self.bg:SetHeight(progressHeight)
+  self.bg:SetWidth(self.width)
+  self.fg:ClearAllPoints()
+  self.fg:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', progressBorder, progressBorder)
+  self.fg:SetHeight(progressHeight - progressBorder *  2)
+  self.status:SetFontObject(progressFont)
+  self.status:SetText(self.objective.quantityString)
+end
+
+T.UpdateWidget.ProgressBar = function (self)
+  local quantity, requiredQuantity = self.objective.value, self.objective.maxValue
+  print('update vals:')
+  for k,v in pairs(self.line) do
+    print(k, v)
+  end
+
+  if self.line.format then
+    self.status:SetFormattedText(self.line.format, quantity, requiredQuantity)
+  end
+
+  local progress = (quantity / requiredQuantity)
+  if progress >= 1 then
+    self.fg:Show()
+    self.fg:SetWidth(self.width  - progressBorder * 2)
+  elseif progress > 0 then
+    self.fg:Show()
+    print('color:', 1-progress*2 , progress*2 - 1,0,1)
+    print('width:', (self.width  -progressBorder * 2) * progress)
+    self.fg:SetTexture(1-progress*2 , progress*2,0,1)
+    self.fg:SetWidth((self.width  -progressBorder * 2) * progress)
+  else
+    self.fg:Hide()
+  end
+end
+
+
+T.InitializeWidget.Hidden = function (self)
+  self.height = 0
+end
+T.UpdateWidget.Hidden = function (self)
+  self.height=  0
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ObjectiveTracker/Widgets.xml	Wed Apr 13 20:19:37 2016 -0400
@@ -0,0 +1,100 @@
+<Ui>
+  <!-- houses objective widget templates -->
+
+  <Frame name="VeneerObjectiveCriteriaProgressBar" virtual="true" hidden="true">
+    <Size x="250" y="30" />
+    <Scripts>
+      <OnLoad>
+        self.widgetType = 'ProgressBar'
+        Veneer.ObjectiveTracker.RegisterWidget(self)
+      </OnLoad>
+      <OnShow>
+        Veneer.ObjectiveTracker.InitializeWidget(self)
+      </OnShow>
+      <OnHide>
+        Veneer.ObjectiveTracker.ReleaseWidget(self)
+      </OnHide>
+      <OnEvent>
+
+      </OnEvent>
+    </Scripts>
+    <Layers>
+      <Layer level="BORDER">
+        <Texture name="$parentBackground" parentKey="bg">
+          <Color r="0" g="0" b="0" a="0.70" />
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" />
+            <Anchor point="TOPRIGHT" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+
+        <Texture name="$parentForeground" parentKey="fg">
+          <Color r="1" g="1" b="1" a="0.7" />
+        </Texture>
+        <Texture name="$parentIcon" parentKey="icon" alphaMode="MOD">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativeKey="$parent.fg" />
+            <Anchor point="BOTTOMRIGHT" relativeKey="$parent.fg" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString name="$parentStatus" parentKey="status" inherits="VeneerCriteriaFont">
+          <Anchors>
+            <Anchor point="CENTER" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="VeneerObjectiveCriteriaEvent" virtual="true" hidden="true">
+    <Size x="250" y="24" />
+    <Scripts>
+      <OnLoad>
+        self.widgetType = 'Event'
+        self.lines = 1
+        Veneer.ObjectiveTracker.RegisterWidget(self)
+      </OnLoad>
+      <OnShow>
+        Veneer.ObjectiveTracker.InitializeWidget(self)
+      </OnShow>
+      <OnHide>
+        Veneer.ObjectiveTracker.ReleaseWidget(self)
+      </OnHide>
+    </Scripts>
+    <Layers>
+      <Layer level="OVERLAY">
+        <FontString name="$parentStatusText" parentKey="status" inherits="VeneerCriteriaFont">
+
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="VeneerObjectiveCriteriaHidden" virtual="true" hidden="true">
+    <Size x="250" y="24" />
+    <Scripts>
+      <OnLoad>
+        self.widgetType = 'Hidden'
+        self.lines = 0
+        Veneer.ObjectiveTracker.RegisterWidget(self)
+      </OnLoad>
+      <OnShow>
+        Veneer.ObjectiveTracker.InitializeWidget(self)
+      </OnShow>
+      <OnHide>
+        Veneer.ObjectiveTracker.ReleaseWidget(self)
+      </OnHide>
+    </Scripts>
+    <Layers>
+      <Layer level="OVERLAY">
+        <FontString name="$parentStatusText" parentKey="status" inherits="VeneerCriteriaFont">
+
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+</Ui>
\ No newline at end of file