changeset 93:98b5e08b75ed v1.4.9

- Fixed quest completion checking and handling - Changed animation method to hopefully stop weird flickering. - Pins are now visible before full reward data is loaded - Filter bar redesigned: - aligned horizontally along the top of the map display - filter buttons display a '+' when there are matches in both current and other zones, and '*' when there only matches in other zones - button tooltips separate local and global quests - button categories are highlighted and labeled when the cursor is over them - Fixed invalid POI targets appearing when the spell targeting cursor is active
author Nenue
date Sat, 15 Apr 2017 11:04:54 -0400
parents df725cba1a6a
children dfd53f7c0fe5
files FilterBar.lua FilterBar.xml FlightMap.lua QuestPOI.lua WorldPlan.lua WorldPlan.xml WorldQuests.lua WorldQuests.xml
diffstat 8 files changed, 1186 insertions(+), 960 deletions(-) [+]
line wrap: on
line diff
--- a/FilterBar.lua	Tue Apr 11 00:44:22 2017 -0400
+++ b/FilterBar.lua	Sat Apr 15 11:04:54 2017 -0400
@@ -15,11 +15,26 @@
 local REWARD_REAGENT = WORLD_QUEST_REWARD_TYPE_FLAG_MATERIALS
 
 
+local filtersUsed
+local filterFill = "Interface\\BUTTONS\\YELLOWORANGE64"
+local filterMask = "Interface\\Minimap\\UI-Minimap-Background"
+
+local HEADERS_SPACING = 3
+local BUTTONS_SPACING = 1
+local BUTTONS_HEIGHT = 20
+local TOGGLE_SIZE = 20
+local HEADERS_HEIGHT = 40
+
 local LE_QUEST_TAG_TYPE_PVP = LE_QUEST_TAG_TYPE_PVP
 local LE_QUEST_TAG_TYPE_PET_BATTLE = LE_QUEST_TAG_TYPE_PET_BATTLE
 local LE_QUEST_TAG_TYPE_DUNGEON = LE_QUEST_TAG_TYPE_DUNGEON
 local LE_QUEST_TAG_TYPE_PROFESSION = LE_QUEST_TAG_TYPE_PROFESSION
 
+local barMouseOver
+local filtersDirty = true
+local layoutDirty = true
+local matchesDirty -- don't flag until first filter loadout
+
 local familiars = {
   [42159] = {npc = 106552, name = 'Nightwatcher Merayl'},
   [40277] = {npc = 97804, name = 'Tiffany Nelson'},
@@ -49,8 +64,15 @@
   showNumber = true,
   numberFontObject = 'WorldPlanNumberFontThin'
 }
+
+local headerNames = {
+  ['rewardType'] = 'Reward Type',
+  ['worldQuestType'] = 'Quest Type',
+  ['isElite'] = 'Elite',
+  ['factionID'] = 'Bounties'
+}
+
 db.DefaultFilters = {
-  { label = 'Filters', texture = "Interface\\WorldMap\\WorldMap-Icon" },
   { filterKey= 'rewardType', cVar = 'worldQuestFilterArtifactPower', filterValue = REWARD_ARTIFACT_POWER, label = 'Artifact Power', texture = "Interface\\ICONS\\inv_7xp_inscription_talenttome01" },
   { filterKey= 'rewardType', cVar = 'worldQuestFilterOrderResources', filterValue = REWARD_CURRENCY,label = 'Order Resources', texture = "Interface\\Icons\\inv_orderhall_orderresources" },
   { filterKey= 'rewardType', cVar = 'worldQuestFilterEquipment', filterValue = REWARD_GEAR, label = 'Equipment', texture = "Interface\\ICONS\\garrison_bluearmorupgrade" },
@@ -58,8 +80,10 @@
   { filterKey= 'rewardType', cVar = 'worldQuestFilterGold', filterValue = REWARD_CASH, label = 'Gold', texture = "Interface\\Buttons\\UI-GroupLoot-Coin-Up" },
   { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PVP, label = 'PvP', texture = "Interface\\Icons\\Ability_PVP_GladiatorMedallion", spacing = 10 },
   { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PET_BATTLE, label = 'Pet Battle', texture = "Interface\\Icons\\PetJournalPortrait", },
-  { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
+  { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_DUNGEON, label = 'Dungeon', texture = "Interface\\LFGFRAME\\BattlenetWorking0", },
   { filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\ICONS\\70_professions_scroll_02", },
+  --{ filterKey = 'isElite', filterValue = true, label = 'Elite', texture = "", desaturated = false},
+  --{ filterKey = 'isElite', filterValue = false, label = 'Not-Elite', texture = "", desaturated = false},
 }
 local defaults = {}
 
@@ -68,15 +92,14 @@
 Module.selectedBountyIndex = {}
 Module.bounties = {}
 Module.filterList = {}
-Module.buttons = {}
+Module.Buttons = {}
+Module.Headers = {}
 Module.cvarFiltersDirty = false
-WorldPlanFilterPinMixin = {}
-local Pin = WorldPlanFilterPinMixin
+WorldPlanFilterButtonMixin = {}
+local Pin = WorldPlanFilterButtonMixin
 
 function Module:OnLoad()
-  self:SetParent(WorldMapFrame.UIElementsFrame)
-  self:ClearAllPoints()
-  self:SetPoint('TOPRIGHT')
+  --self:SetParent(WorldMapFrame.UIElementsFrame)
   WorldPlan:AddHandler(self)
   --[[for index, info in ipairs(db.DefaultFilters) do
     info.zone = db.DefaultFilterType
@@ -85,37 +108,65 @@
     WorldPlan:AddTypeInfo(self,index, info)
   end
   --]]
+
+
 end
 
 
 function Module:OnEvent(event, arg)
   print('|cFF00FF88'..self:GetName()..':OnEvent()', event)
   if (event == 'QUEST_LOG_UPDATE') and arg then
-    if db.QuestsByID[arg] then
-      if db.QuestsByID[arg].factionID then
-
-      end
-    end
+    filtersDirty = true
+    self:Refresh()
   end
-
-  self.isStale = true
 end
 
 local bountyIndex
 local debug_headers = {}
-
+local ToggleButton = {}
 function Module:Setup()
   print('|cFF00FF88'..self:GetName()..':Setup()')
   self.isStale = true
-  self:SetShown(true)
+  self:SetParent(WorldMapFrame.UIElementsFrame)
+  --self:SetPoint('BOTTOMLEFT')
+  for k,v in pairs( ToggleButton) do
+    self.Toggle:SetScript(k,v)
+  end
+  self.Toggle:SetSize(TOGGLE_SIZE, TOGGLE_SIZE)
+
 end
 
 
-function Module:OnUpdate()
+function Module:OnUpdate(sinceLast)
   if self.isStale then
-    wprint('|cFF00FF00pushing update')
+    print('|cFF00FF00pushing update')
     self:Refresh()
   end
+
+  barMouseOver = self:IsMouseOver()
+  if barMouseOver or filtersUsed then
+
+    self.toAlpha = 1
+    self.Backdrop:Show()
+  else
+    self.toAlpha = 0.25
+    self.Backdrop:Hide()
+  end
+  local cAlpha = self:GetAlpha()
+  if cAlpha ~= self.toAlpha then
+    if cAlpha > self.toAlpha then
+      cAlpha = cAlpha - sinceLast*4
+      if cAlpha <= self.toAlpha then
+        cAlpha = self.toAlpha
+      end
+    else
+      cAlpha = cAlpha + sinceLast*4
+      if cAlpha >= self.toAlpha then
+        cAlpha = self.toAlpha
+      end
+    end
+  end
+  self:SetAlpha(cAlpha)
 end
 
 function Module:OnMapInfo(isBrokenIsle, isZoomedOut, mapAreaID, isNewMap, isMapOpen)
@@ -123,72 +174,142 @@
   if not isBrokenIsle then
     self:SetShown(false)
   else
-    if self:IsShown() then
+    self:SetShown(true)
+    if isMapOpen then
       self:Refresh()
+    else
+      matchesDirty = true
+      layoutDirty = true
     end
-    self:SetShown(true)
   end
 end
 
 function Module:OnShow()
   print('|cFF00FF88'..self:GetName()..':OnShow()')
-  if self.isStale then
-    self:Refresh()
+  self:Refresh()
+end
+
+local IsBountyCriteria = function(poiFrame, questID)
+  return IsQuestCriteriaForBounty(poiFrame.questID, questID)
+end
+
+local tinsert, GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID = tinsert, GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID
+
+function Module:OnConfigUpdate()
+
+  ToggleButton.OnShow(self.Toggle)
+end
+
+function Module:Reset()
+  self:UpdateFilters('SUMMARY_RESET')
+  self:UpdateMatches('SUMMARY_RESET')
+  self:UpdateLayout('SUMMARY_RESET')
+end
+
+function Module:Refresh()
+  self:UpdateFilters('SUMMARY_REFRESH')
+  self:UpdateMatches('SUMMARY_REFRESH')
+  self:UpdateLayout('SUMMARY_REFRESH')
+end
+
+local questResults = {{} }
+db.FilterList = {}
+
+function Module:UpdateFilters(event)
+
+  print('|cFF00FFFF'..self:GetName()..':GetFilters()', event)
+
+  wipe(db.FilterList)
+
+  for index, info in ipairs(db.DefaultFilters) do
+    tinsert(db.FilterList, info)
+  end
+  self.bounties = db.Bounties
+  self.BountyFilters = {}
+  for index, data in ipairs(self.bounties) do
+    if not IsQuestComplete(data.questID) then
+
+      local info = self.BountyFilters[index]
+      if not info then
+        info  = {}
+        self.BountyFilters[index] = info
+        layoutDirty = true
+      else
+        if info.questID ~= data.questID then
+          layoutDirty = true
+        end
+      end
+
+      local questTitle = GetQuestLogTitle(GetQuestLogIndexByID(data.questID))
+      info.filterKey = 'factionID'
+      info.filterFunc = IsBountyCriteria
+      info.filterValue = data.questID
+      info.label = questTitle
+      info.texture = data.icon
+      print('loading emissary', questTitle)
+
+      tinsert(db.FilterList, info)
+      --{ filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
+    end
+
   end
 end
 
-local tinsert, GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID = tinsert, GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID
-function Module:GetFilters()
+function Module:UpdateMatches(event)
+  print('|cFF00FF00UpdateMatches()', event)
+  local quests = db.QuestsByID
+  local questsForMap = db.QuestsByZone[db.currentMapID] or quests
 
-  print('|cFF00FFFF'..self:GetName()..':GetFilters()')
+  for index, info in ipairs(db.FilterList) do
+    info.GlobalMatches = info.GlobalMatches or {}
+    info.LocalMatches = info.LocalMatches or {}
+    wipe(info.GlobalMatches)
+    wipe(info.LocalMatches)
+    print(info.filterKey, info.filterValue, info.filterFunc and 'func test' or 'compare')
+    for questID, pin in pairs(quests) do
+      print('', questID, pin.dataLoaded, (not IsQuestComplete(questID)))
+      if pin.dataLoaded and not IsQuestComplete(questID) then
 
-  wipe(self.filterList)
+        local keyName, keyValue = info.filterKey, info.filterValue
+        local isMatch
+        if info.filterFunc then
 
-  for index, info in ipairs(db.DefaultFilters) do
-    tinsert(self.filterList, info)
-  end
-  self.bounties, self.numBounties = GetQuestBountyInfoForMapID(db.currentMapID)
-  self.BountyFilters = {}
-  for index, data in ipairs(self.bounties) do
-    local info = self.BountyFilters[index]
-    if not info then
-      info  = {}
-      self.BountyFilters[index] = info
+          isMatch = info.filterFunc(pin, keyValue)
+          print('  running special function, result =', isMatch)
+
+        elseif pin[keyName] and (pin[keyName] == keyValue) then
+          isMatch = true
+          print('  rote match')
+        end
+        if isMatch then
+          tinsert(info.GlobalMatches, pin)
+          if questsForMap[questID] then
+            print('  local map')
+            tinsert(info.LocalMatches, pin)
+          end
+
+        end
+      end
+
     end
-
-    local questTitle = GetQuestLogTitle(GetQuestLogIndexByID(data.questID))
-
-    info.filterKey = 'factionID'
-    info.filterValue = data.factionID
-    info.label = questTitle
-    info.texture = data.icon
-    print('loading emissary', questTitle)
-
-    tinsert(self.filterList, info)
-    --{ filterKey= 'worldQuestType', filterValue = LE_QUEST_TAG_TYPE_PROFESSION, label = 'Profession', texture = "Interface\\LFGFRAME\\UI-LFR-PORTRAIT", },
+    print('global', #info.GlobalMatches, 'local', #info.LocalMatches)
   end
 end
 
-function Module:Reset()
-  self:GetFilters()
-end
+function Module:UpdateLayout(event)
+  print('|cFF00FF88UpdateLayout()|r', event, 'currentMap=',db.currentMapID)
 
-function Module:Refresh()
-  self:GetFilters()
-  self:Update()
-end
+  self.filtersSelected = nil
 
-local filterFill = "Interface\\BUTTONS\\YELLOWORANGE64"
-local filterMask = "Interface\\Minimap\\UI-Minimap-Background"
+  local currentHeader
+  local relativeFrame = self
+  local lastKey
 
-local questResults = {{}}
-function Module:Update()
-  local blocks = self.buttons
+  local numHeaders = 0
+  local numButtons = 0
 
-  local relativeFrame = self
-
-    local numHeaders = 0
-  print('|cFF00FF88'..self:GetName()..':Update()|r', 'currentMap=',db.currentMapID)
+  local layoutWidth = TOGGLE_SIZE + HEADERS_SPACING * 2
+  local headerWidth = HEADERS_SPACING
 
 
   local layout = db.DefaultFilterType
@@ -201,104 +322,140 @@
   end
   print(n, 'pins to work with')
 
+
+  filtersUsed = nil
   local firstCvar, lastCvar
-  for index, info in ipairs(self.filterList) do
-    local numQuestsHere = 0
-    local numQuestsTotal = 0
-    info.questList = info.questList or {}
-    wipe(info.questList)
-    print(info.filterKey, info.filterValue)
+  local isFirst = true
+  for index, info in ipairs(db.FilterList) do
+    --print('num here', numQuestsHere, numQuestsTotal)
 
-    for questID, pin in pairs(db.QuestsByID) do
-      --print(pin.worldQuestType ~= LE_QUEST_TAG_TYPE_PROFESSION, (db.Config.ShowAllProfessionQuests or pin.isKnownProfession))
-      if pin.used then
-
-        print(pin.title, mapQuests[questID])
-        if (pin.worldQuestType ~= LE_QUEST_TAG_TYPE_PROFESSION) or (db.Config.ShowAllProfessionQuests or pin.isKnownProfession) then
-
-          if not info.filterKey then
-            if mapQuests[questID] then
-                numQuestsHere = numQuestsHere + 1
-            end
-            numQuestsTotal = numQuestsTotal + 1
-          elseif pin[info.filterKey] == info.filterValue then
-            if mapQuests[questID] then
-              numQuestsHere = numQuestsHere + 1
-              tinsert(info.questList, pin)
-            end
-            numQuestsTotal = numQuestsTotal + 1
-          end
-        end
-      end
-
-    end
-    --print('num here', numQuestsHere, numQuestsTotal)
-    info.totalQuests = numQuestsTotal
 
     --print(tostring(index).. ' ("'..tostring(info.label)..'" f('.. tostring(info.filterKey).. '='..tostring(info.filterValue) .. '), '..tostring(numQuests)..')')
 
-    if numQuestsTotal >= 1 then
-      numHeaders = numHeaders + 1
-      local button = blocks[numHeaders]
-      if not blocks[numHeaders] then
-        button = CreateFrame('Button', 'WorldPlanFilterButton'..numHeaders, self, 'WorldPlanFilterPin')
-        button:SetSize(32,20)
-        button.icon:SetTexCoord(0.1,.9,.1,(1 * (20/32)))
+    if #info.GlobalMatches >= 1 then
+      if info.filterKey ~= lastKey then
+
+        numHeaders = numHeaders + 1
+        local nextHeader = self.Headers[numHeaders] or CreateFrame('Frame', 'FilterHeader' .. numHeaders, self,  'WorldPlanFilterHeader')
+        if currentHeader then
+          nextHeader:SetPoint('TOPLEFT', currentHeader, 'TOPRIGHT', 0, 0)
+
+          currentHeader:SetSize(headerWidth - BUTTONS_SPACING + HEADERS_SPACING, HEADERS_HEIGHT)
+          layoutWidth = layoutWidth + headerWidth
+          headerWidth = HEADERS_SPACING
+        else
+          nextHeader:SetPoint('TOPLEFT', TOGGLE_SIZE + HEADERS_SPACING * 2, 0)
+
+        end
+
+        print('  begin header '..numHeaders, 'layout width =', floor(layoutWidth+.5))
+        isFirst = true
+        currentHeader = nextHeader
+        currentHeader.Backdrop:SetHeight(BUTTONS_HEIGHT *2)
+        currentHeader.Label:SetText(headerNames[info.filterKey])
+        relativeFrame = currentHeader
+      end
+
+      numButtons = numButtons + 1
+      local button = self.Buttons[numButtons]
+      if not button then
+        button = CreateFrame('Button', 'FilterButton'..numButtons, self, 'WorldPlanFilterButton')
+        button:SetSize(32,BUTTONS_HEIGHT)
+        button.icon:SetTexCoord(0.1,.9,.1,(1 * (BUTTONS_HEIGHT/32)))
 
         button.RewardBorder:ClearAllPoints()
         button.RewardBorder:SetPoint('TOPLEFT', button, 'TOPLEFT')
         button.RewardBorder:SetPoint('BOTTOMRIGHT', button, 'BOTTOMRIGHT')
-
-
-        blocks[numHeaders] = button
       end
 
       button.info = info
-      button.numQuestsTotal = numQuestsTotal
-      button.numQuestsHere = numQuestsHere
-      button.questList = info.questList
-      button.isFirst = (numHeaders == 1)
+      button.numQuestsTotal = #info.GlobalMatches
+      button.numQuestsHere = #info.LocalMatches
+      button.GlobalMatches = info.GlobalMatches
+      button.LocalMatches = info.LocalMatches
+      button.isFirst = isFirst
       button:SetID(index)
-      button.spacing = ((relativeFrame.cVar and (not info.cVar)) or (relativeFrame.filterKey ~= info.filterKey)) and 5 or 1
+      button:SetParent(currentHeader)
       button.relativeFrame = relativeFrame
       button:Refresh()
       button:Show()
       relativeFrame = button
+      headerWidth = headerWidth + button:GetWidth() + BUTTONS_SPACING
 
+      isFirst = false
       if info.cVar then
         firstCvar = firstCvar or button
         lastCvar = button
       end
-
+        lastKey = info.filterKey
     end
   end
 
   self.numHeaders = numHeaders
-  for i = numHeaders + 1, #blocks do
-    if blocks[i] then
-      blocks[i]:Hide()
-      wipe(blocks[i].questList)
+  for i = numButtons + 1, #self.Buttons do
+    if self.Buttons[i] then
+      self.Buttons[i]:Hide()
+      wipe(self.Buttons[i].LocalMatches)
+      wipe(self.Buttons[i].GlobalMatches)
+    end
+  end
+  for i = numHeaders + 1, #self.Headers do
+    if self.Headers[i] then
+      self.Headers[i]:Hide()
     end
   end
 
 
+  if currentHeader then
+    currentHeader:SetSize(headerWidth - BUTTONS_SPACING + HEADERS_SPACING, HEADERS_HEIGHT)
+    layoutWidth = layoutWidth + headerWidth + HEADERS_SPACING
+  end
 
+  self:SetSize(layoutWidth, BUTTONS_HEIGHT + BUTTONS_SPACING * 2)
+  self:ClearAllPoints()
+  self:SetPoint('TOP')
   self.isStale = nil
+  layoutDirty = nil
 end
 
 function Module:Cleanup()
   -- hide trailing buttons
 end
 
-local rgbWhite = {r = 1, g= 1, b= 1, hex = '|cFFFFFFFF'}
+local rgbWhite = {r = 1, g= 1, b= 1, hex = '|cFFFFFFFF' }
+local found = {}
 function Pin:OnEnter()
-  if #self.questList >= 1 then
-    GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
+  if #self.GlobalMatches >= 1 then
+    GameTooltip:SetOwner(self, 'ANCHOR_BOTTOMRIGHT')
     GameTooltip:AddLine(self.info.label)
-    for index, pin in ipairs(self.questList) do
-      local colorInfo = (pin.quality and ITEM_QUALITY_COLORS[pin.quality]) or rgbWhite
-      GameTooltip:AddLine('|T'.. tostring(pin.itemTexture)..':16:16|t ' .. tostring(pin.title) ..(pin.cheevos and " |cFFFFFF00!|R" or ''), colorInfo.r, colorInfo.g, colorInfo.b)
+    wipe(found)
+
+    if self.numQuestsHere >= 1 then
+      if self.numQuestsHere < self.numQuestsTotal then
+        GameTooltip:AddLine('This Zone', 1, 1, 0)
+      end
+      for index, pin in ipairs(self.LocalMatches) do
+        local colorInfo = (pin.quality and ITEM_QUALITY_COLORS[pin.quality]) or rgbWhite
+        found[pin] = pin
+        GameTooltip:AddLine('|T'.. tostring(pin.itemTexture)..':16:16|t ' .. tostring(pin.title) ..(pin.cheevos and " |cFFFFFF00!|R" or ''), 0, 1, 0)
+      end
     end
+
+    if self.numQuestsHere < self.numQuestsTotal then
+      if self.numQuestsHere >= 1 then
+        GameTooltip:AddLine(' ')
+      end
+      GameTooltip:AddLine('Other Maps', 1, 1, 0)
+      for index, pin in ipairs(self.GlobalMatches) do
+        if not found[pin] then
+          local colorInfo = (pin.quality and ITEM_QUALITY_COLORS[pin.quality]) or rgbWhite
+          found[pin] = pin
+          GameTooltip:AddLine('|T'.. tostring(pin.itemTexture)..':16:16|t ' .. tostring(pin.title) ..(pin.cheevos and " |cFFFFFF00!|R" or ''), 1, 1, 1)
+        end
+      end
+    end
+
+
     GameTooltip:AddLine(self.numQuestsTotal .. ' total')
     GameTooltip:Show()
   end
@@ -317,46 +474,58 @@
   self.tagID = info.tagID
 
   self.icon:SetTexture(info.texture)
-  self.count:SetText(self.numQuestsHere)
+
+  if (self.numQuestsHere == 0) and (self.numQuestsTotal >= 1) then
+    self.count:SetText('*'..self.numQuestsTotal)
+    self.count:SetTextColor(0,1,1)
+  elseif self.numQuestsHere < self.numQuestsTotal then
+    self.count:SetText(self.numQuestsHere..'+')
+    self.count:SetTextColor(1,1,0)
+  else
+    self.count:SetText(self.numQuestsHere)
+    self.count:SetTextColor(1,1,1)
+  end
+
   self.cVar = info.cVar
-
   self.itemTexture = self.texture
 
   self:ClearAllPoints()
   if self.isFirst then
-    self:SetPoint('TOPRIGHT', self.relativeFrame, 'TOPRIGHT', -5, -42)
+    self:SetPoint('TOPLEFT', self.relativeFrame, 'TOPLEFT', HEADERS_SPACING, -HEADERS_SPACING)
   else
-    self:SetPoint('TOPLEFT', self.relativeFrame, 'BOTTOMLEFT', 0, -(self.spacing or 0))
+    self:SetPoint('LEFT', self.relativeFrame, 'RIGHT', BUTTONS_SPACING, 0)
   end
-  print('anchor', self.relativeFrame:IsShown(), self:GetPoint(1))
+  --print('anchor', self.relativeFrame:IsShown(), self:GetPoint(1))
 
   self.icon:SetDesaturated(self.numQuestsHere == 0)
 
   local r, g, b, a = 0,0,0,1
   local desaturated = false
-  if (self.numQuestsHere > 0) then
+  if (self.numQuestsHere > 0) or db.UsedFilters[self.filterKey] then
+
     if self.cVar then
       if GetCVarBool(self.cVar) then
-        self.count:SetTextColor(1,1,1)
+        --self.count:SetTextColor(1,1,1)
         r,g,b,a = 0, 0, 0, 1
       else
-        self:GetParent().cvarFiltersDirty = true
-        self.count:SetTextColor(1,0,0)
+        filtersUsed = true
+        --self.count:SetTextColor(1,0,0)
         self.icon:SetDesaturated(true)
         r,g,b,a = 1, 0, 0, 0.5
       end
     else
       if db.UsedFilters[self.filterKey] then
+        filtersUsed = true
         if db.UsedFilters[self.filterKey] == self.filterValue then
-          self.count:SetTextColor(0,1,0)
+          --self.count:SetTextColor(0,1,0)
           r, g, b = 0, 1, 0
         else
-          self.count:SetTextColor(1,0,0)
+          --self.count:SetTextColor(1,0,0)
           r, g, b = 1, 0, 0
         end
       else
 
-        self.count:SetTextColor(1,1,1)
+        --self.count:SetTextColor(1,1,1)
         if self.filterKey == 'worldQuestType' then
           r, g, b = 0, 0, 1
         elseif self.filterKey == 'factionID' then
@@ -375,12 +544,19 @@
   self:RegisterForClicks('AnyUp')
   self:SetFrameStrata('HIGH')
   self:SetFrameLevel(151)
-  self:SetScript('OnUpdate', nil)
   self.questList = {}
   -- WORLD_MAP_UPDATE and PLAYER_ENTERING_WORLD are passed down from a higher level
 end
 
 function Pin:OnUpdate ()
+  local group = self:GetParent()
+  if group:IsMouseOver() and barMouseOver then
+    group.Backdrop:Show()
+    group.Label:Show()
+  else
+    group.Backdrop:Hide()
+    group.Label:Hide()
+  end
 end
 
 -- shift-click: reset filter
@@ -436,8 +612,8 @@
 
       -- check the visible filters and consider it clean if they're all lit
       parent.cvarFiltersDirty = false
-      for i, info in ipairs(parent.filterList) do
-        if info.cVar and (#info.questList >= 1) then
+      for i, info in ipairs(db.FilterList) do
+        if info.cVar and (#info.GlobalMatches >= 1) then
           print(info.cVar, GetCVarBool(info.cVar))
           if GetCVarBool(info.cVar) == false then
             parent.cvarFiltersDirty = true
@@ -475,4 +651,30 @@
     --WorldPlan:print('Setting filter(s):', table.concat(filtered_report, ', '))
   end
   WorldPlan:Refresh(true)
+end
+function ToggleButton:OnEnter()
+
+  GameTooltip:SetOwner(self, 'ANCHOR_BOTTOMRIGHT')
+  GameTooltip:AddLine('Toggle Pins')
+  GameTooltip:Show()
+end
+function ToggleButton:OnLeave()
+
+  if GameTooltip:IsOwned(self) then
+    GameTooltip:Hide()
+  end
+end
+function ToggleButton:OnShow()
+  self:SetChecked(db.Config.EnablePins and true or false)
+end
+function ToggleButton:OnHide()
+  if GameTooltip:IsOwned(self) then
+    GameTooltip:Hide()
+  end
+
+end
+function ToggleButton:OnClick()
+  --print(self:GetChecked())
+  db.Config.EnablePins = self:GetChecked()
+  _G.WorldPlan:OnConfigUpdate()
 end
\ No newline at end of file
--- a/FilterBar.xml	Tue Apr 11 00:44:22 2017 -0400
+++ b/FilterBar.xml	Sat Apr 15 11:04:54 2017 -0400
@@ -1,17 +1,113 @@
 <Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
 ..\FrameXML\UI.xsd">
   <Script file="FilterBar.lua" />
-  <Frame name="$parentSummary" parent="WorldPlan" mixin="WorldPlanSummaryMixin">
+  <Frame name="$parentSummary" parent="WorldPlan" mixin="WorldPlanSummaryMixin" hidden="true">
+    <Size x="24" y="24" />
+    <Anchors>
+      <Anchor point="BOTTOMLEFT" />
+    </Anchors>
     <Scripts>
       <OnLoad method="OnLoad" />
       <OnEvent method="OnEvent" />
       <OnShow method="OnShow" />
+      <OnUpdate method="OnUpdate" />
     </Scripts>
 
-    <Size x="24" y="24" />
+    <Layers>
+      <Layer level="BACKGROUND">
 
+        <Texture parentKey="Backdrop" hidden="true">
+          <Size y="40" />
+          <Anchors>
+            <Anchor point="LEFT" />
+            <Anchor point="RIGHT" />
+            <Anchor point="TOP" />
+          </Anchors>
+          <Gradient orientation="VERTICAL">
+            <MinColor r="0" g="0" b="0" a="0" />
+            <MaxColor r="0" g="0" b="0" a=".7" />
+          </Gradient>
+          <Color a="1" r="1" g="1" b="1" />
+        </Texture>
+      </Layer>
+    </Layers>
+
+    <Frames>
+      <CheckButton name="$parentToggle" parentKey="Toggle" >
+        <Size x="20" y="20" />
+        <Anchors>
+          <Anchor point="TOPLEFT" x="3" y="-3" />
+        </Anchors>
+        <Layers>
+          <Layer level="BACKGROUND">
+            <Texture setAllPoints="false">
+              <Anchors>
+                <Anchor point="TOPRIGHT" x="-1" y="-1" />
+                <Anchor point="BOTTOMLEFT" x="1" y="1" />
+              </Anchors>
+              <Color a="1" r="0" g="0" b="0" />
+            </Texture>
+          </Layer>
+        </Layers>
+        <NormalTexture setAllPoints="true">
+          <Color a=".25" r="0" g="0" b="0" />
+        </NormalTexture>
+        <CheckedTexture setAllPoints="false" file="Interface\BUTTONS\UI-CheckBox-Check">
+          <Anchors>
+            <Anchor point="TOPRIGHT" x="-1" y="-1" />
+            <Anchor point="BOTTOMLEFT" x="1" y="1" />
+          </Anchors>
+        </CheckedTexture>
+      </CheckButton>
+    </Frames>
   </Frame>
-  <Button name="WorldPlanFilterPin" virtual="true" flattenRenderLayers="true" mixin="WorldPlanFilterPinMixin">
+  <Frame name="WorldPlanFilterHeader" parentArray="Headers" virtual="true" enableMouse="true">
+    <Size x="24" y="30" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="Backdrop" hidden="true">
+          <Anchors>
+            <Anchor point="LEFT" />
+            <Anchor point="RIGHT" />
+            <Anchor point="TOP" />
+          </Anchors>
+          <Gradient orientation="VERTICAL">
+            <MinColor r="0" g="0" b="0" a=".5" />
+            <MaxColor r="0" g="0" b="0" a="1" />
+          </Gradient>
+          <Color a="1" r="1" g="1" b="1" />
+        </Texture>
+        <Texture parentKey="Edge1">
+          <Size x="1" y="24" />
+          <Anchors>
+            <Anchor point="TOPLEFT"  />
+          </Anchors>
+          <Gradient orientation="VERTICAL">
+            <MinColor r="1" g="1" b="1" a="0" />
+            <MaxColor r="1" g="1" b="1" a=".5" />
+          </Gradient>
+          <Color a="1" r="1" g="1" b="1" />
+        </Texture>
+        <Texture parentKey="Edge2">
+          <Size x="1" y="24" />
+          <Anchors>
+            <Anchor point="TOPRIGHT"  />
+          </Anchors>
+          <Gradient orientation="VERTICAL">
+            <MinColor r="1" g="1" b="1" a="0" />
+            <MaxColor r="1" g="1" b="1" a=".5" />
+          </Gradient>
+          <Color a="1" r="1" g="1" b="1" />
+        </Texture>
+        <FontString inherits="GameFontNormal" parentKey="Label" hidden="true">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" x="2" y="2" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Frame>
+  <Button name="WorldPlanFilterButton" parentArray="Buttons" virtual="true" mixin="WorldPlanFilterButtonMixin">
     <Scripts>
       <OnClick method="OnClick" />
       <OnLoad method="OnLoad" />
@@ -25,7 +121,7 @@
     </Scripts>
     <Layers>
       <Layer level="BACKGROUND">
-        <Texture parentKey="RewardBorder" setAllPoints="true" />
+        <Texture parentKey="RewardBorder" setAllPoints="true" alphaMode="ADD" />
       </Layer>
       <Layer level="ARTWORK">
         <Texture parentKey="icon">
--- a/FlightMap.lua	Tue Apr 11 00:44:22 2017 -0400
+++ b/FlightMap.lua	Sat Apr 15 11:04:54 2017 -0400
@@ -119,6 +119,7 @@
                 pin.throttle = 1
                 pin:ClearAllPoints()
                 pin:SetPoint('CENTER', frame, 'CENTER')
+                frame:SetSize(pin:GetSize())
                 --print(pin.Overlay:IsShown(), pin.Overlay:GetPoint(1))
               end
             end
--- a/QuestPOI.lua	Tue Apr 11 00:44:22 2017 -0400
+++ b/QuestPOI.lua	Sat Apr 15 11:04:54 2017 -0400
@@ -26,7 +26,7 @@
 local HaveQuestRewardData = HaveQuestRewardData
 
 
-local pairs, ipairs, tinsert, unpack, select = pairs, ipairs, tinsert, unpack, select
+local pairs, ipairs, tinsert, tremove, unpack, select = pairs, ipairs, tinsert, tremove, unpack, select
 local floor, mod, tostring, tonumber, GetSuperTrackedQuestID = floor, mod, tostring, tonumber, GetSuperTrackedQuestID
 local GameTooltip = GameTooltip
 local GetItemIcon = GetItemIcon
@@ -40,8 +40,8 @@
 local dprint = DEVIAN_WORKSPACE and function(...) _G.print('WQData', ...) end or nop
 local QuestPOI = WorldPlanPOIMixin
 
-local pinBaseIndex = 1500
-local overlayBaseIndex = 1580
+local pinBaseIndex = 1320
+local overlayBaseIndex = 1380
 local previousHighlight
 
 local DATA_DEBUG = false
@@ -252,6 +252,50 @@
 
 end
 
+function QuestPOI:OnLoad()
+  --qprint('|cFF00FF88'..self:GetName()..':OnLoad()|r',db.Config)
+  self.debugTimer = 4
+  self.title = '|cFF0088FF' .. RETRIEVING_DATA..'|r'
+  self.count = self.Overlay.count
+  self.timeLabel = self.Overlay.timeLabel
+  self.Description = self.Overlay.Description
+  self.updateRate = PIN_REQUEST_DELAY
+  self.itemName = '|cFF0088FF' .. RETRIEVING_DATA..'|r'
+
+
+  self.IconBackdrop:SetVertexColor(0,0,0,1)
+  self.Overlay:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 4)
+  self.Overlay:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 0, -4)
+end
+
+function QuestPOI:OnShow ()
+  if self.isStale then
+    --print('|cFF00FF00refresh on show')
+    self:Refresh('POI_ONSHOW_STALE')
+  end
+  self:RegisterEvent('QUEST_TURNED_IN')
+  self:RegisterEvent('QUEST_LOG_UPDATE')
+  self:HideOrShowFrames(true)
+end
+
+function QuestPOI:OnEvent(event, questID)
+  if (self.questID == questID) and IsQuestComplete(self.questID) then
+    db.log(self.questID .. " Marked completed.")
+    self:Release()
+  end
+end
+
+function QuestPOI:OnHide()
+  --DEFAULT_CHAT_FRAME:AddMessage('|cFFFFFF00'..self:GetName()..'|r:OnHide()')
+  self:HideOrShowFrames(false)
+  -- reset flags
+  self:SetAlpha(db.PinAlpha)
+  self.isAnimating = nil
+  if db.Config.DebugEnabled then
+    db.log(tostring(self.questID) .. ' ' .. tostring(self.title) .. "\n" .. tostring(REWARD_TYPE_NAMES[self.rewardType]) .. ' ' .. tostring(self.itemName) .. ' ' .. tostring(self.itemNumber) .. "\n|cFFFF4400" .. (self.hideReason or 'NO_MESSAGE') .. "|r\n|cFF00FFFF" ..  debugstack(2,3,0) .. '|r')
+  end
+  self.hideReason = nil
+end
 
 function QuestPOI:OnEnter()
   if self.filtered and (self.questID ~= GetSuperTrackedQuestID()) then
@@ -328,6 +372,15 @@
   --print(WorldMapTooltip:GetParent())
   --print(WorldMapTooltip:IsVisible())
 end
+
+function QuestPOI:OnMouseDown(button)
+  if button == 'RightButton' then
+    SetSuperTrackedQuestID(nil)
+  else
+    TaskPOI_OnClick(self, button)
+  end
+end
+
 function QuestPOI:OnLeave()
   if self.filtered and (self.questID ~= GetSuperTrackedQuestID()) then
     return
@@ -336,6 +389,74 @@
   WorldMapTooltip:Hide();
 end
 
+local updateTime, markTime
+function QuestPOI:OnUpdate (sinceLast)
+  -- control update check intervals
+
+  if self.animating then
+    local alpha = self.icon:GetAlpha() + sinceLast*3
+
+    self.animateTime = (self.animateTime or 0) + sinceLast
+    if alpha >= 1 then
+      alpha = 1
+      print('fade over', self.animateTime)
+      self.animating = nil
+      self.animateTime = nil
+
+    end
+
+    self.icon:SetAlpha(alpha)
+    self.RewardBorder:SetAlpha(alpha)
+  end
+
+  self.throttle = (self.throttle or self.updateRate) + sinceLast
+  if self.throttle >= self.updateRate then
+    -- factor overtime into the throttle timer
+    self.throttle = self.throttle - self.updateRate
+  else
+    return
+  end
+  --@debug@
+  if DATA_DEBUG then
+    self.debugTimer = self.debugTimer - sinceLast
+    if self.debugTimer >= 0 then
+      print(self.debugTimer)
+      return
+    end
+  end
+  --@end-debug@
+
+  -- query for reward data if it wasn't found in the original scan
+
+  if not self.dataLoaded then
+    local dataLoaded = self:GetData()
+    if dataLoaded and not tContains(db.UpdatedPins, self) then
+      -- self.PendingFade:Stop()
+      -- scale info from the parent module is needed, so deal with it there
+      print('|cFF00FF88  queueing for update')
+      self.isNew = true
+      tinsert(db.UpdatedPins, self)
+    else
+
+      --print('|cFFFF4400OnUpdate(|r'..self:GetID()..'|cFFFF4400)|r poll failed')
+    end
+    return
+  end
+
+  if self.maxAlertLevel then
+    self:UpdateStatus()
+  end
+end
+
+
+function QuestPOI:StartFading()
+  if not self.animating then
+    self.animating = true
+    self.icon:SetAlpha(0)
+    self.RewardBorder:SetAlpha(0)
+  end
+end
+
 -- attempt to pull pin data
 function QuestPOI:GetData ()
   --dprint('|cFF00FF88'..self:GetID()..':GetData()|r')
@@ -352,7 +473,7 @@
     return false
   else
     self.title, self.factionID, self.capped = questTitle, factionID, capped
-    print(questTitle, factionID, capped)
+    --print(questTitle, factionID, capped)
     -- set tag details
     local worldQuestType
     self.tagID, self.tagName, worldQuestType, self.rarity, self.isElite, self.tradeskillLineIndex = GetQuestTagInfo(questID);
@@ -397,18 +518,6 @@
 local ID_RESOURCES = 1220
 local ID_LEGIONFALL = 1342
 
-function QuestPOI:Reset()
-  self.isActive = nil
-  self.complete = nil
-  self.used = nil
-  self.dataLoaded = nil
-  self.rewardType = nil
-  self.itemTexture = nil
-  self.itemName = nil
-  self.itemNumber = nil
-  self:SetShown(false)
-end
-
 --- Returns true if data has changed (either from loading in or qualifications changed)
 function QuestPOI:UpdateRewards()
   local questID = self.questID
@@ -484,8 +593,6 @@
 
           if itemID then
             local itemName, itemLink, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture, sellPrice, classID, subclassID = GetItemInfo(itemID);
-
-
             if ( classID == LE_ITEM_CLASS_WEAPON or classID == LE_ITEM_CLASS_ARMOR or (classID == LE_ITEM_CLASS_GEM and subclassID == LE_ITEM_GEM_ARTIFACTRELIC) ) then
               rewardType = REWARD_GEAR
               rewardIcon = texture
@@ -496,26 +603,8 @@
               rewardType = REWARD_ARTIFACT_POWER
               rewardIcon = texture
               rewardName = name
-              rewardCount = 1
+              rewardCount = self:UpdateArtifactPower(itemLink)
               foundPrimary = true
-
-              WorldPlanTooltip:SetOwner(self, 'ANCHOR_NONE')
-              WorldPlanTooltip:SetHyperlink(itemLink)
-              for i = 1, WorldPlanTooltip:NumLines() do
-                local line = _G['WorldPlanTooltipTextLeft' .. i]
-                local text = line and line:GetText()
-                local ap = text and text:gsub(',', ''):gsub(' million', '000000'):match('(%d+) '..ARTIFACT_POWER)
-                if ap then
-                  rewardCount = tonumber(ap)
-                  --dprint(ap)
-                end
-
-              end
-
-              if WorldPlanTooltipTextLeft2 then
-                local text = WorldPlanTooltipTextLeft2:GetText()
-              end
-
               --dprint('is an AP token')
             elseif classID == LE_ITEM_CLASS_TRADEGOODS then
               rewardType = REWARD_REAGENT
@@ -524,8 +613,6 @@
               rewardCount = numItems
               foundPrimary = true
             end
-
-
             --dprint('  reward', i, name, " |T"..tostring(texture)..":12:12|t", quality, isUsable, itemID)
             tinsert(rewardItems, {
               name = name,
@@ -582,70 +669,26 @@
   end
 end
 
--- run from OnShow if .isNew is set
 
-function QuestPOI:OnAnimStart()
-  --qprint('|cFFFFFF00OnAnimStart(|r'..self:GetID()..'|cFFFFFF00)|r', self.fadeEvent)
-  self:Refresh('FADE_IN_START')
-  self.isNew = nil
-end
-
-function QuestPOI:TryToFade(event)
-  if self.FadeIn:IsPlaying() then
-    --qprint('|cFFFF4400TryToFade('..self:GetID()..'|cFFFF4400)|r stopping because already in progress')
+function QuestPOI:UpdateArtifactPower(rewardLink)
+  if not (rewardLink or self.rewardLink) then
     return
   end
 
-  self.fadeEvent = event
-  if self.dataLoaded then
-    --qprint('|cFFFFFF00TryToFade('..self:GetID()..'|cFFFFFF00)|r', event)
-    self:SetAlpha(0)
-    self.FadeIn.FadeIn:SetToAlpha(db.PinAlpha)
-    self.FadeIn:Play()
-  else
+  self.rewardLink = rewardLink or self.rewardLink
 
-    --qprint('|cFFFF4400TryToFade('..self:GetID()..'|cFFFF4400)|r stopping because not loaded')
-  end
-end
-
-function QuestPOI:OnAnimStop()
-  self:SetAlpha(db.PinAlpha)
-end
-
-function QuestPOI:OnShow ()
-
-  if self.isNew then
-    self:TryToFade('POI_ONSHOW_NEW')
-  elseif not self.FadeIn:IsPlaying() then
-    print('|cFF00FF00Alpha correction')
-    self:SetAlpha(db.PinAlpha) -- fix stuck alpha
-    if self.isStale then
-      --print('|cFF00FF00refresh on show')
-      self:Refresh('POI_ONSHOW_STALE')
+  local rewardCount
+  WorldPlanTooltip:SetOwner(self, 'ANCHOR_NONE')
+  WorldPlanTooltip:SetHyperlink(rewardLink or self.rewardLink)
+  for i = 1, WorldPlanTooltip:NumLines() do
+    local line = _G['WorldPlanTooltipTextLeft' .. i]
+    local text = line and line:GetText()
+    local ap = text and text:gsub(',', ''):gsub(' million', '000000'):match('([%d%.]+) '..ARTIFACT_POWER)
+    if ap then
+      rewardCount = tonumber(ap)
     end
   end
-  self:RegisterEvent('QUEST_LOG_UPDATE')
-  self:ShowFrames()
-end
-
-function QuestPOI:OnEvent(event)
-  if not TQ_IsActive(self.questID) then
-    self:UnregisterEvent('QUEST_LOG_UPDATE')
-    self.hideReason = 'Hiding self because quest is inactive.'
-    self:SetShown(false)
-  end
-end
-
-function QuestPOI:OnHide()
-  --DEFAULT_CHAT_FRAME:AddMessage('|cFFFFFF00'..self:GetName()..'|r:OnHide()')
-  self:HideFrames()
-  -- reset flags
-  self:SetAlpha(db.PinAlpha)
-  self.isAnimating = nil
-  if db.Config.DebugEnabled then
-    db.log(tostring(self.questID) .. ' ' .. tostring(self.title) .. "\n" .. tostring(REWARD_TYPE_NAMES[self.rewardType]) .. ' ' .. tostring(self.itemName) .. ' ' .. tostring(self.itemNumber) .. "\n|cFFFF4400" .. (self.hideReason or 'NO_MESSAGE') .. "|r\n|cFF00FFFF" ..  debugstack(2,3,0) .. '|r')
-  end
-  self.hideReason = nil
+  return rewardCount
 end
 
 -- Applies position and sizing parameters to the pin data
@@ -653,7 +696,7 @@
   --dprint(self:GetName()..':SetAnchor()', owner, dX, dY, scaleFactor, self.filtered, self.used)
   if not self.used then
     self.hideReason = 'SetAnchor() on an unused frame.'
-    self:HideFrames()
+    self:HideOrShowFrames(false)
     return
   end
 
@@ -696,108 +739,19 @@
 
 end
 
--- Show/Hide the text overlays associated with the quest pin; they aren't hierarchically linked
-function QuestPOI:ShowFrames()
-  if not self:IsShown() then
-    -- print('|cFFFFFF00' ..self:GetName()..':ShowFrames()')
-    -- do not SetShown() here
-  end
-  self.Overlay:SetShown(true)
-  self.count:SetShown(true)
-  self.timeLabel:SetShown(true)
-end
-
-function QuestPOI:HideFrames()
-  if self:IsShown() then
-    if not self.hideReason then
-      self.hideReason = "HideFrames() called"
-    end
-  end
-  self.Overlay:SetShown(false)
-  self.count:SetShown(false)
-  self.timeLabel:SetShown(false)
-end
-
-function QuestPOI:OnLoad()
-  --qprint('|cFF00FF88'..self:GetName()..':OnLoad()|r',db.Config)
-  self.debugTimer = 4
-  self.title = '|cFF0088FF' .. RETRIEVING_DATA..'|r'
-  self.isPending = true
-  self.count = self.Overlay.count
-  self.timeLabel = self.Overlay.timeLabel
-  self.Description = self.Overlay.Description
-  self.updateRate = PIN_REQUEST_DELAY
-  self.itemName = '|cFF0088FF' .. RETRIEVING_DATA..'|r'
-
-  self.Overlay:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 4)
-  self.Overlay:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 0, -4)
-end
-
-function QuestPOI:OnMouseDown(button)
-  if button == 'RightButton' then
-    SetSuperTrackedQuestID(nil)
-  else
-    TaskPOI_OnClick(self, button)
-  end
-end
-
-local updateTime, markTime
-function QuestPOI:OnUpdate (sinceLast)
-  -- control update check intervals
-
-
-  self.throttle = (self.throttle or self.updateRate) + sinceLast
-  if self.throttle >= self.updateRate then
-    -- factor overtime into the throttle timer
-    self.throttle = self.throttle - self.updateRate
-  else
-    return
-  end
-  --@debug@
-  if DATA_DEBUG then
-    self.debugTimer = self.debugTimer - sinceLast
-    if self.debugTimer >= 0 then
-      print(self.debugTimer)
-      return
-    end
-  end
-  --@end-debug@
-
-  -- query for reward data if it wasn't found in the original scan
-
-  if not self.dataLoaded then
-
-    local dataLoaded = self:GetData()
-    if dataLoaded and not tContains(db.UpdatedPins, self) then
-      -- self.PendingFade:Stop()
-      -- scale info from the parent module is needed, so deal with it there
-      --print('|cFF00FF88  queueing for update')
-      tinsert(db.UpdatedPins, self)
-    else
-
-      --print('|cFFFF4400OnUpdate(|r'..self:GetID()..'|cFFFF4400)|r poll failed')
-    end
-    return
-  end
-
-  if self.maxAlertLevel then
-    self:UpdateStatus()
-  end
-
-end
-
 -- Non-hieriarchical display states, checked separately from used/filtered states
 function QuestPOI:GetCriteriaState()
   local isCriteria, isBounty, isSpellTarget
-  if self.factionID then
-    for index, bounty in pairs(db.Bounties) do
-      if IsQuestCriteriaForBounty(self.questID, bounty.questID) then
-        isCriteria = true
-        if db.selectedBounty == bounty then
-          isBounty = true
-        end
-        --dprint('|cFF00FF88Criteria:|r', self.questID, bounty.questID, isCriteria, isBounty)
+
+
+
+  for index, bounty in pairs(db.Bounties) do
+    if (not IsQuestComplete(bounty.questID)) and IsQuestCriteriaForBounty(self.questID, bounty.questID) then
+      isCriteria = true
+      if db.selectedBounty == bounty then
+        isBounty = true
       end
+      --dprint('|cFF00FF88Criteria:|r', self.questID, bounty.questID, isCriteria, isBounty)
     end
   end
   isSpellTarget = IsQuestIDValidSpellTarget(self.questID)
@@ -812,184 +766,6 @@
   end
 end
 
--- Called at static intervals and with Refresh
-function QuestPOI:UpdateStatus()
-  -- update time elements
-  self.isActive = TQ_IsActive(self.questID)
-
-  if self.isActive then
-    local tl = self.alertLevel
-    local timeLeft = TQ_GetQuestTimeLeftMinutes(self.questID)
-    if timeLeft > 0 then
-
-      local text, timeState = WorldPlan:GetTimeInfo(timeLeft, self.maxAlertLevel)
-      if tl ~= timeState then
-        tl = timeState
-        self.timeLabel:SetText(text)
-      end
-    end
-
-    local border = (self.isBounty or self.isCriteria) and self.RewardBorder or self.HighlightBorder
-
-    if tl and (timeLeft < 120) then
-      border:SetVertexColor(1,0,0,0.7)
-    else
-      border:SetVertexColor(0,0,0,0.7)
-    end
-    self.alertLevel = tl
-    self.timeLabel:SetShown(self.worldQuest and (self.maxAlertLevel >= 1))
-  else
-    self.hideReason = "No longer active."
-    self:HideFrames()
-
-  end
-end
-
-
-function QuestPOI:Refresh (event)
-
-  print('|cFF00FF88Refresh(|r'..self:GetID()..'|cFF00FF88)|r', event, self.title)
-
-  local style = DEFAULT_STYLE
-  if self.dataLoaded and not self.filtered then
-    style = REWARD_TYPE_STYLES[self.rewardType]
-  else
-    style = MINIMIZED_STYLE
-  end
-
-  local currentWidth = style.iconWidth or DEFAULT_STYLE.iconWidth
-
-
-
-  local borderWidth = style.borderWidth or DEFAULT_STYLE.borderWidth
-  local highlightWidth = style.highlightWidth or DEFAULT_STYLE.highlightWidth
-  local tagSize = style.TagSize or DEFAULT_STYLE.TagSize
-  local hideIcon = style.hideIcon or DEFAULT_STYLE.hideIcon
-  local borderColor = style.border or DEFAULT_STYLE.border
-  local textColor = style.textColor or DEFAULT_STYLE.textColor
-  local questID = self.questID
-  local iconBorder = self.RewardBorder
-  local trackingBorder = self.HighlightBorder
-  local icon = self.icon
-  local count = self.count
-  local hideNumbers = style.hideNumber or DEFAULT_STYLE.hideNumber
-
-  local tagIcon = self.tagIcon
-  self.maxAlertLevel = style.maxAlertLevel or DEFAULT_STYLE.maxAlertLevel
-
-
-  if self.itemName then
-    if self.itemNumber and (self.itemNumber > 1) and (not hideNumbers) then
-      local numberString = self.itemNumber
-      if self.itemNumber >= 1000000 then
-        numberString = (floor(self.itemNumber/100000)/10) .. 'M'
-      elseif self.itemNumber >= 10000 then
-        numberString = floor(self.itemNumber/1000) .. 'k'
-      elseif self.itemNumber >= 1000 then
-        local numeral = floor(self.itemNumber/1000)
-        local decimal = mod(self.itemNumber, 1000)
-        numberString = numeral
-        if decimal > 100 then
-          numberString = numberString .. '.' .. tostring(floor(decimal/100))
-        end
-        numberString = numberString .. 'k'
-      end
-
-      self.count:SetText(numberString)
-      self.count:SetTextColor(unpack(textColor))
-      self.count:Show()
-    else
-      self.count:SetText(nil)
-      self.count:Hide()
-    end
-  else
-    self.count:Hide()
-  end
-
-  if db.Config.ShowVerboseInfo then
-    self.Description:SetText(self.title .. "\n" .. floor(self.x*100+.5) .. "," .. floor(self.y*100+.5))
-  end
-
-
-  icon:SetSize(currentWidth, currentWidth)
-  icon:SetMask(style.iconMask or DEFAULT_STYLE.iconMask)
-  if self.itemTexture then
-    --iconBorder:SetTexture(WORLD_QUEST_BORDER)
-
-    if hideIcon then
-      icon:SetTexture(PENDING_ICON)
-      icon:SetDesaturated(true)
-      icon:SetVertexColor(unpack(borderColor))
-    else
-      icon:SetTexture(self.itemTexture)
-      icon:SetDesaturated(false)
-      icon:SetVertexColor(1, 1, 1)
-    end
-  else
-    --
-    icon:SetTexture(PENDING_ICON)
-    icon:SetDesaturated(true)
-    icon:SetVertexColor(unpack(borderColor))
-  end
-  local borderMask = style.borderMask or DEFAULT_STYLE.borderMask
-  local borderSize = currentWidth + (borderWidth * 2) + (self.isCriteria and 2 or 0)
-
-
-  iconBorder:SetSize(borderSize, borderSize)
-  iconBorder:SetMask(borderMask)
-  iconBorder:SetTexture(PENDING_BORDER)
-
-  iconBorder:SetDesaturated(true)
-
-  local highlightSize = borderSize + (highlightWidth * 2)
-  trackingBorder:SetSize(highlightSize, highlightSize)
-  trackingBorder:SetMask(borderMask)
-  trackingBorder:SetTexture(PENDING_BORDER)
-
-  self:SetSize(borderSize, borderSize)
-
-
-
-  iconBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
-  trackingBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
-
-  self.tagIcon:SetShown((not self.filtered) and true or false)
-  self.tagIcon:SetAtlas(self.tagAtlas)
-  self.EliteBorder:SetShown(self.isElite and not self.filtered)
-
-  self:UpdateSize()
-  self:UpdateStatus()
-  self.isStale = nil
-
-  if self.isBounty then
-    --print('is bounty')
-    iconBorder:SetVertexColor(trackingBorder:GetVertexColor())
-    trackingBorder:SetVertexColor(unpack(BORDER_SELECTED_BOUNTY))
-  elseif self.isCriteria then
-    --print('is criteria of a bounty')
-    iconBorder:SetVertexColor(trackingBorder:GetVertexColor())
-    trackingBorder:SetVertexColor(unpack(BORDER_CRITERIA))
-  else
-
-    iconBorder:SetVertexColor(unpack(borderColor))
-    trackingBorder:SetVertexColor(1,1,1,1)
-  end
-
-  if SpellCanTargetQuest() then
-    if IsQuestIDValidSpellTarget(self.questID) then
-      icon:SetVertexColor(1,1,1)
-    else
-      icon:SetVertexColor(1,0,0,1)
-    end
-    self:EnableMouse(false)
-  else
-    self:EnableMouse(true)
-  end
-
-  -- signal filter info update
-  WorldPlanSummary.isStale = true
-end
-
 local cvar_check = {
   [REWARD_CASH] = 'worldQuestFilterGold',
   [REWARD_ARTIFACT_POWER] = 'worldQuestFilterArtifactPower',
@@ -1044,57 +820,250 @@
   self:SetShown(true)
 end
 
---- Fixes icons upon size update
-function QuestPOI:UpdateSize ()
+function QuestPOI:Refresh (event)
+  print('|cFF00FF88Refresh(|r'..self:GetID()..'|cFF00FF88)|r', event, self.title)
 
-  --qprint('|cFF00BB88'..self:GetID()..'|r:UpdateSize()', self.style, self.subStyle)
-  return
-  --[[
-  local style = self.style
-  local subStyle = self.subStyle
-  local icon = self.icon
+
+  local style = DEFAULT_STYLE
+  if self.filtered then
+    print('choose minimized')
+    style = MINIMIZED_STYLE
+  elseif self.dataLoaded then
+    print('choose reward type')
+    style = REWARD_TYPE_STYLES[self.rewardType]
+  else
+    print('choose default')
+  end
+
+  local currentWidth = style.iconWidth or DEFAULT_STYLE.iconWidth
+
+
+
+  local borderWidth = style.borderWidth or DEFAULT_STYLE.borderWidth
+  local highlightWidth = style.highlightWidth or DEFAULT_STYLE.highlightWidth
+  local tagSize = style.TagSize or DEFAULT_STYLE.TagSize
+  local hideIcon = style.hideIcon or DEFAULT_STYLE.hideIcon
+  local borderColor = style.border or DEFAULT_STYLE.border
+  local textColor = style.textColor or DEFAULT_STYLE.textColor
+  local questID = self.questID
   local iconBorder = self.RewardBorder
   local trackingBorder = self.HighlightBorder
-  local tag = self.tagIcon
+  local icon = self.icon
+  local count = self.count
+  local hideNumbers = style.hideNumber or DEFAULT_STYLE.hideNumber
 
-  local iconWidth = subStyle.iconWidth
-  local borderWidth = iconWidth + (subStyle.borderWidth * 2)
-  local highlightWidth = borderWidth + (subStyle.highlightWidth * 2)
-  local iconTexture = self.itemTexture
 
+  local tagIcon = self.tagIcon
+  self.maxAlertLevel = style.maxAlertLevel or DEFAULT_STYLE.maxAlertLevel
 
-  self:SetSize(highlightWidth, highlightWidth)
-  if self.questID == GetSuperTrackedQuestID() then
-    highlightWidth = highlightWidth + 2
-    if self.filtered then
-      self:SetAlpha(db.PinAlpha * 0.5)
+  if self.dataLoaded then
+    print('new pin, has data, cue fade')
+    if self.isNew then
+      self:StartFading()
+      self.isNew = nil
+    end
+  else
+    if not self.animating then
+    print('new pin, but no data, hide icon')
+      self.icon:SetAlpha(0)
+      self.RewardBorder:SetAlpha(0)
+    end
+  end
+
+  if self.itemName then
+    if self.itemNumber and (self.itemNumber > 1) and (not hideNumbers) then
+      local numberString = self.itemNumber
+      if self.itemNumber >= 1000000 then
+        numberString = (floor(self.itemNumber/100000)/10) .. 'M'
+      elseif self.itemNumber >= 10000 then
+        numberString = floor(self.itemNumber/1000) .. 'k'
+      elseif self.itemNumber >= 1000 then
+        local numeral = floor(self.itemNumber/1000)
+        local decimal = mod(self.itemNumber, 1000)
+        numberString = numeral
+        if decimal > 100 then
+          numberString = numberString .. '.' .. tostring(floor(decimal/100))
+        end
+        numberString = numberString .. 'k'
+      end
+
+      self.count:SetText(numberString)
+      self.count:SetTextColor(unpack(textColor))
+      self.count:Show()
     else
-      self:SetAlpha(db.PinAlpha)
+      self.count:SetText(nil)
+      self.count:Hide()
+    end
+  else
+    self.count:Hide()
+  end
+
+  if db.Config.ShowVerboseInfo then
+    self.Description:SetText(self.title .. "\n" .. floor(self.x*100+.5) .. "," .. floor(self.y*100+.5))
+  end
+
+
+  icon:SetSize(currentWidth, currentWidth)
+  icon:SetMask(style.iconMask or DEFAULT_STYLE.iconMask)
+  if self.itemTexture then
+    --iconBorder:SetTexture(WORLD_QUEST_BORDER)
+
+    if hideIcon then
+      icon:SetTexture(PENDING_ICON)
+      icon:SetDesaturated(true)
+      icon:SetVertexColor(unpack(borderColor))
+    else
+      icon:SetTexture(self.itemTexture)
+      icon:SetDesaturated(false)
+      icon:SetVertexColor(1, 1, 1)
+    end
+  else
+    --
+    --icon:SetTexture(PENDING_ICON)
+    --icon:SetDesaturated(true)
+    --icon:SetVertexColor(unpack(borderColor))
+  end
+  local borderMask = style.borderMask or DEFAULT_STYLE.borderMask
+  local borderSize = currentWidth + (borderWidth * 2) + (self.isCriteria and 2 or 0)
+
+
+  iconBorder:SetSize(borderSize, borderSize)
+
+  iconBorder:SetMask(borderMask)
+  iconBorder:SetTexture(PENDING_BORDER)
+
+  iconBorder:SetDesaturated(true)
+
+  local highlightSize = borderSize + (highlightWidth * 2)
+  trackingBorder:SetSize(highlightSize, highlightSize)
+  trackingBorder:SetMask(borderMask)
+  trackingBorder:SetTexture(PENDING_BORDER)
+
+  self:SetSize(borderSize, borderSize)
+  self.IconBackdrop:SetSize(currentWidth, currentWidth)
+
+
+
+  iconBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
+  trackingBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
+
+  self.tagIcon:SetShown((not self.filtered) and true or false)
+  self.tagIcon:SetAtlas(self.tagAtlas)
+  self.EliteBorder:SetShown(self.isElite and not self.filtered)
+
+  self:UpdateStatus()
+
+  if self.isBounty then
+    --print('is bounty')
+    iconBorder:SetVertexColor(trackingBorder:GetVertexColor())
+    trackingBorder:SetVertexColor(unpack(BORDER_SELECTED_BOUNTY))
+  elseif self.isCriteria then
+    --print('is criteria of a bounty')
+    iconBorder:SetVertexColor(trackingBorder:GetVertexColor())
+    trackingBorder:SetVertexColor(unpack(BORDER_CRITERIA))
+  else
+
+    iconBorder:SetVertexColor(unpack(borderColor))
+    trackingBorder:SetVertexColor(0,0,0,.5)
+  end
+
+  if SpellCanTargetQuest() then
+    if IsQuestIDValidSpellTarget(self.questID) then
+      icon:SetVertexColor(1,1,1)
+    else
+      icon:SetVertexColor(1,0,0,1)
+    end
+    self:EnableMouse(false)
+  else
+    self:EnableMouse(true)
+  end
+  self.isStale = nil
+
+  -- signal filter info update
+  WorldPlanSummary.isStale = true
+end
+
+-- Called at static intervals and with Refresh
+function QuestPOI:UpdateStatus()
+  -- update time elements
+  self.isActive = TQ_IsActive(self.questID)
+
+  if self.isActive then
+    local tl = self.alertLevel
+    local timeLeft = TQ_GetQuestTimeLeftMinutes(self.questID)
+    if timeLeft > 0 then
+
+      local text, timeState = WorldPlan:GetTimeInfo(timeLeft, self.maxAlertLevel)
+      if tl ~= timeState then
+        tl = timeState
+        self.timeLabel:SetText(text)
+      end
+    end
+
+    local border = (self.isBounty or self.isCriteria) and self.RewardBorder or self.HighlightBorder
+
+    if tl and (timeLeft < 120) then
+      border:SetVertexColor(1,0,0,0.7)
+    else
+      border:SetVertexColor(0,0,0,0.7)
+    end
+    self.alertLevel = tl
+    self.timeLabel:SetShown(self.worldQuest and (self.maxAlertLevel >= 1))
+  else
+    self.hideReason = "No longer active."
+    self:HideOrShowFrames(false)
+
+  end
+end
+
+-- Show/Hide the text overlays associated with the quest pin; they aren't hierarchically linked
+function QuestPOI:HideOrShowFrames(isShown)
+  if not isShown then
+    -- print('|cFFFFFF00' ..self:GetName()..':HideOrShowFrames()')
+    -- do not SetShown() here
+    if not self.hideReason then
+      self.hideReason = "HideOrShowFrames() called"
+    end
+  end
+  self.Overlay:SetShown(isShown)
+  self.count:SetShown(isShown)
+  self.timeLabel:SetShown(isShown)
+end
+
+function QuestPOI:Release()
+
+  self.hideReason = 'Released by script.'
+  self:SetShown(false)
+  if self.questID then
+    db.QuestsByID[self.questID] = nil
+    for _, map in pairs(db.QuestsByZone) do
+      map[self.questID] = nil
+    end
+    self.questID = nil
+  end
+  self.isActive = nil
+  self.complete = nil
+  self.used = nil
+  self.dataLoaded = nil
+  self.rewardType = nil
+  self.itemTexture = nil
+  self.itemName = nil
+  self.itemNumber = nil
+  self:SetShown(false)
+  self.animating = nil
+  self.icon:SetAlpha(0)
+  self.RewardBorder:SetAlpha(0)
+  self:UnregisterEvent('QUEST_TURNED_IN')
+  self:UnregisterEvent('QUEST_LOG_UPDATE')
+
+  for i, pin in ipairs(db.UsedPins) do
+    if pin == self then
+      tremove(db.UsedPins, i)
+      break
     end
 
   end
 
-  if self.rarity and WORLD_QUEST_QUALITY_COLORS[self.rarity] then
-    highlightWidth = highlightWidth + self.rarity
-  end
-  self.tagIcon:SetSize(self.tagSize, self.tagSize)
-  icon:SetSize(iconWidth, iconWidth)
-  iconBorder:SetSize(borderWidth, borderWidth)
-  trackingBorder:SetSize(highlightWidth, highlightWidth)
-
-
-  iconBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
-  trackingBorder:SetPoint('CENTER', (style.x or 0), (style.y or 0))
-
-
-  if (subStyle.showNumber and self.itemNumber) and style.hasNumeric then
-    self.count:SetTextColor(unpack(style.numberRGB))
-    if subStyle.numberFontObject then
-      --wqprint('change font', _G[subStyle.numberFontObject]:GetName())
-      self.count:SetFontObject(_G[subStyle.numberFontObject])
-    end
-  else
-    self.count:SetText(nil)
-  end
-  --]]
+  tinsert(db.FreePins, self)
+  WorldPlan.dataFlush = true
 end
\ No newline at end of file
--- a/WorldPlan.lua	Tue Apr 11 00:44:22 2017 -0400
+++ b/WorldPlan.lua	Sat Apr 15 11:04:54 2017 -0400
@@ -263,10 +263,6 @@
       db.DefaultConfig[frame:GetName()] = frame.defaults
     end
 
-    frame.GetTypeInfo = function(frame, typeID)
-      return self:GetTypeInfo(frame, typeID)
-    end
-
     frame.owningFrame = self
   else
 
@@ -351,26 +347,14 @@
 
   local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo()
 
-
-  if db.isContinentMap ~= isContinent then
-    for _, pin in pairs(db.QuestsByID) do
-       pin.isStale = true
-    end
-  end
   local isMapOpen = WorldMapFrame:IsShown()
-  if not isMapOpen then
-    return
-  end
-
-  local isNewMap = (mapAreaID ~= db.currentMapID) or (isMapOpen ~= db.isMapOpen) or (db.isMicroDungeon ~= isMicroDungeon)
-
+  local isNewMap = (mapAreaID ~= db.currentMapID) or (isMapOpen ~= db.isMapOpen) or (db.isMicroDungeon ~= isMicroDungeon) or (db.isContinentMap ~= isContinent)
 
   db.isMicroDungeon = isMicroDungeon
   db.isMapOpen = isMapOpen
   db.currentMapID = mapAreaID
   db.isContinentMap = isContinent
   db.isBrokenIsle = isBrokenIsle
-  db.useContinentType = (WorldMapDetailFrame:GetScale() < 1)
 
   for _, module in ipairs(db.OrderedModules) do
     if module.OnMapInfo then
@@ -394,6 +378,14 @@
   else
     if (event == 'WORLD_MAP_UPDATE') or (event == 'PLAYER_ENTERING_WORLD') or (event == 'PLAYER_LOGIN') then
       print('|cFFFF4400currentMapID =', db.currentMapID, ...)
+      if event == 'PLAYER_ENTERING_WORLD' then
+        -- start from scratch
+        db.isMicroDungeon = nil
+        db.isMapOpen = nil
+        db.currentMapID = nil
+        db.isContinentMap = nil
+        db.isBrokenIsle = nil
+      end
 
 
       self:SetCurrentMap(event .. ' ' .. GetTime())
--- a/WorldPlan.xml	Tue Apr 11 00:44:22 2017 -0400
+++ b/WorldPlan.xml	Sat Apr 15 11:04:54 2017 -0400
@@ -36,10 +36,11 @@
     </Layers>
   </Button>
 
-  <Button name="WorldPlanQuestPin" virtual="true" hidden="true" frameStrata="TOOLTIP" mixin="WorldPlanPOIMixin">
+  <Button name="WorldPlanQuestPin" virtual="true" hidden="true" mixin="WorldPlanPOIMixin">
     <Animations>
       <AnimationGroup parentKey="FadeIn" setToFinalAlpha="true" looping="NONE">
-        <Alpha parentKey="FadeIn" duration="0.45" fromAlpha="0" toAlpha="1" order="1" />
+        <Alpha parentKey="Icon" childKey="icon" duration="0.45" fromAlpha="0" toAlpha="1" order="1" />
+        <Alpha parentKey="Border" childKey="RewardBorder" duration="0.45" fromAlpha="0" toAlpha="1" order="1" />
         <Scripts>
           <OnPlay>
             self:GetParent():OnAnimStart()
@@ -52,10 +53,6 @@
           </OnFinished>
         </Scripts>
       </AnimationGroup>
-      <AnimationGroup parentKey="PendingFade" setToFinalAlpha="true" looping="REPEAT">
-        <Alpha parentKey="FadeOut" duration="0.72" fromAlpha="1" toAlpha="0" order="1" />
-        <Alpha parentKey="FadeIn" duration="0.72" fromAlpha="0" toAlpha="1" order="2" />
-      </AnimationGroup>
     </Animations>
     <Layers>
       <Layer level="BACKGROUND" textureSubLevel="-2">
@@ -71,20 +68,27 @@
           </Anchors>
         </Texture>
       </Layer>
-      <Layer level="BACKGROUND" textureSubLevel="1">
-        <Texture parentKey="RewardBorder" file="Interface\UNITPOWERBARALT\Generic1Target_Circular_Frame" desaturated="true">
+      <Layer level="BACKGROUND" textureSubLevel="-1">
+        <Texture parentKey="RewardBorder" file="Interface\Minimap\UI-Minimap-Background" desaturated="true" alpha="0">
           <Anchors>
             <Anchor point="CENTER" />
           </Anchors>
         </Texture>
       </Layer>
-      <Layer level="BORDER">
-        <Texture parentKey="icon" setAllPoints="true">
+      <Layer textureSubLevel="0">
+
+        <Texture parentKey="IconBackdrop" file="Interface\Minimap\UI-Minimap-Background">
           <Anchors>
             <Anchor point="CENTER" />
           </Anchors>
         </Texture>
-
+      </Layer>
+      <Layer level="ARTWORK" textureSubLevel="2">
+        <Texture parentKey="icon" setAllPoints="true" alpha="0">
+          <Anchors>
+            <Anchor point="CENTER" />
+          </Anchors>
+        </Texture>
       </Layer>
       <Layer level="OVERLAY">
         <Texture parentKey="dot" hidden="true" setAllPoints="false">
--- a/WorldQuests.lua	Tue Apr 11 00:44:22 2017 -0400
+++ b/WorldQuests.lua	Sat Apr 15 11:04:54 2017 -0400
@@ -26,10 +26,10 @@
 local GetQuestLogRewardInfo = GetQuestLogRewardInfo
 local GetCurrentMapAreaID, GetMapInfo, GetMapNameByID = GetCurrentMapAreaID, GetMapInfo, GetMapNameByID
 local GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID, IsQuestComplete = GetQuestBountyInfoForMapID, GetQuestLogTitle, GetQuestLogIndexByID, IsQuestComplete
-local IsQuestCriteriaForBounty = IsQuestCriteriaForBounty
+local HaveQuestRewardData = HaveQuestRewardData
+local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation
 
 local ToggleButton = {}
-local callbacks = {}
 local BROKEN_ISLES_ID, DALARAN_ID, AZSUNA_ID, VALSHARAH_ID, HIGHMOUNTAIN_ID, STORMHEIM_ID, SURAMAR_ID, EOA_ID = 1007, 1014, 1015,1018, 1024, 1017, 1033, 1096
 local WORLD_QUEST_MAPS = { [DALARAN_ID] = 'Dalaran70',  [AZSUNA_ID] = 'Azsuna',  [VALSHARAH_ID] = "Val'sharah",
   [HIGHMOUNTAIN_ID] = 'Highmountain', [STORMHEIM_ID] = 'Stormheim',  [SURAMAR_ID] = 'Suramar',  [EOA_ID] = 'EyeOfAszhara', }
@@ -43,19 +43,60 @@
 
 local BountyBoard = WorldMapFrame.UIElementsFrame.BountyBoard
 local ActionButton = WorldMapFrame.UIElementsFrame.ActionButton
+local defaults = {}
 
+local continentScanned
+local layoutDirty = true
+local bountiesDirty = true
+local artifactPowerDirty = true
+local currentScale = WorldMapDetailFrame:GetScale()
+local canTargetQuests
+local isDataLoaded = true
+local artifactKnowledgeLevel
+local superTrackedQuestID
+local lastRefresh
+local refreshReason
+
+local bountyQuests = {}
+local bountyInfo = {}
+local bountyDisplayLocation, bountyLockedQuestID, selectedBountyIndex, selectedBountyQuestID
+
+local totalPins = 0
+local numShown = 0
+local numLoaded = 0
+local numOverlays = 1
+local scaleConstant = 1
 local pinBaseIndex = 1500
 local overlayBaseIndex = 1580
-local layoutDirty = true
-local bountiesDirty = true
-local currentScale = WorldMapDetailFrame:GetScale()
-local canTargetQuests
-local numShown = 0
-local numLoaded = 0
-local isDataLoaded = true
-local numOverlays = 1
-local scaleConstant = 1
-Module.TasksByID = {}
+
+local artifactKnowldegeSpells = {
+  [207856] = true,
+  [209203] = true,
+  [209204] = true,
+  [209205] = true,
+  [209206] = true,
+  [209207] = true,
+  [209208] = true,
+  [209209] = true,
+  [209210] = true,
+  [209211] = true,
+  [209212] = true,
+  [219978] = true,
+  [227852] = true,
+  [236477] = true,
+  [236489] = true,
+  [236302] = true,
+  [236488] = true,
+  [236490] = true,
+  [240475] = true,
+  [243176] = true,
+  [243177] = true,
+  [243178] = true,
+  [243182] = true,
+  [243183] = true,
+  [243187] = true,
+  [245133] = true,
+}
 
 --%debug%
 local SetTimedCallbackForAllPins = function(seconds, callback)
@@ -66,61 +107,86 @@
   end)
 end
 
+function Module:OnLoad()
+  --print('|cFFFF4400'..self:GetName()..':OnLoad()')
 
-function Module:Setup()
-  --print('|cFFFF4400'..self:GetName()..':Setup()')
-  for mapID, mapName in pairs(WORLD_QUEST_MAPS) do
-    db.QuestsByZone[mapID] = {}
+  self:SetParent(WorldMapFrame.UIElementsFrame)
+  WorldPlan:AddHandler(self, defaults)
+
+  for areaID, fileName in pairs(WORLD_QUEST_MAPS) do
+    db.QuestsByZone[areaID] = {}
   end
-  for target, arg in pairs(callbacks) do
-    --print(type(target))
-    if type(target) == 'table' then
-      local callerName = target:GetName() or tostring(target)
-      for name, method  in pairs(arg) do
-        --print(callerName, arg)
-        hooksecurefunc(target, name, function(...)
-          self:OnSecureHook(callerName .. '.' .. name, method, ...)
-        end)
+
+  -- WORLD_MAP_UPDATE and PLAYER_ENTERING_WORLD are passed down from a higher level
+  self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL')
+  self:RegisterEvent('SUPER_TRACKED_QUEST_CHANGED')
+  self:RegisterEvent('SKILL_LINES_CHANGED')
+  self:RegisterEvent('ARTIFACT_UPDATE')
+  self:RegisterEvent('QUEST_LOG_UPDATE')
+  self:RegisterEvent('UNIT_SPELLCAST_STOP')
+end
+
+function Module:OnEvent (event, ...)
+  print('|cFFFFFF00OnEvent() '..event..'|r', GetTime(), ...)
+  if (event == 'QUEST_LOG_UPDATE') then
+    self:UpdateBounties(event)
+  elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then
+    local questID = ...
+    if questID and db.QuestsByID[questID] then
+      db.QuestsByID[questID]:Release()
+    end
+    self:Refresh(event)
+  elseif event == 'SKILL_LINES_CHANGED' or event == 'CURRENT_SPELL_CAST_CHANGED' then
+    self:Refresh(event)
+  elseif event == 'ARTIFACT_UPDATE' then
+    self:UpdateArtifactPower()
+  elseif event == 'MODIFIER_STATE_CHANGED' then
+    self:UpdateModifierState()
+  elseif event == 'SUPER_TRACKED_QUEST_CHANGED' then
+    if superTrackedQuestID and db.QuestsByID[superTrackedQuestID] then
+      db.QuestsByID[superTrackedQuestID].isStale = true
+    end
+    local newID = GetSuperTrackedQuestID()
+    if newID and db.QuestsByID[newID] then
+      db.QuestsByID[newID].isStale = true
+    end
+  elseif event == 'UNIT_SPELLCAST_STOP' then
+    local name, _, _, _, spellID = ...
+    if artifactKnowldegeSpells[spellID] then
+      db.log('AK spellcast ended ' .. tostring(name) .. ' ('.. tostring(spellID)..')')
+      self:UpdateArtifactPower()
+    end
+  end
+end
+
+function Module:OnUpdate(sinceLast)
+  if WorldPlanData.DebugEnabled then
+    if self.refreshBenchMarkTicker then
+      --print(self.refreshBenchMarkTicker)
+      self.refreshBenchMarkTicker =   self.refreshBenchMarkTicker - 1
+
+      if self.refreshBenchMarkTicker == 0 then
+
+        self.refreshTime = floor((GetTime() - self.refreshBenchMark) * 1000)
+        self.debugMessage:SetText(self.refreshTime)
+        self.refreshBenchMarkTicker = nil
       end
     else
-      hooksecurefunc(target, function(...)
-        self:OnSecureHook(target, arg, ...)
-      end)
+      self.refreshBenchMark = GetTime()
     end
   end
 
-  self.Status = CreateFrame('Frame', nil, self)
-  self.Status:SetPoint('TOPLEFT', WorldMapFrame.UIElementsFrame, 'TOPLEFT', 0, 0)
-  self.Status:SetPoint('BOTTOMRIGHT', WorldMapFrame.UIElementsFrame, 'TOPRIGHT', 0, -4)
-  self.Status.t = self.Status:CreateTexture(nil, 'OVERLAY')
-  self.Status.b = self.Status:CreateTexture(nil, 'BACKGROUND')
-  self.Status.b:SetColorTexture(0,0,0,.25)
-  self.Status.b:SetAllPoints(self.Status)
-  self.Status.t:SetColorTexture(1,1,1,.5)
-  self.Status.t:SetPoint('TOP')
-  self.Status.t:SetPoint('BOTTOM')
-  self.Status.t:SetPoint('LEFT')
-  local translationEnd, translationStart
-  self.Status:SetScript('OnUpdate', function(status)
-    local translateTo
-    if numLoaded < numShown then
-      translateTo = (numLoaded/numShown) * status:GetWidth()
-      status.t:SetWidth(translateTo)
-    else
-      translateTo = status:GetWidth()
-      status.t:SetWidth(translateTo)
-    end
-  end)
-
-  self:SetAllPoints(WorldMapFrame.UIElementsFrame)
-  for k,v in pairs( ToggleButton) do
-    self.Toggle:SetScript(k,v)
+  if self.filtersDirty or self.isStale then
+    self:Refresh()
   end
 
-  self:UpdateBounties('SETUP')
+  if #db.UpdatedPins >= 1 then
+    --print('|cFF00FF88pending update', #db.UpdatedPins)
+    self:UpdateNext()
+  end
+end
 
-  self:Show()
-end
+local callbacks = {}
 callbacks.ClickWorldMapActionButton = function(WorldQuests)
   WorldQuests:Refresh('CLICK_MAP_ACTION_BUTTON')
 end
@@ -145,6 +211,43 @@
   WorldQuests:Refresh('CASTING_STATE_CHANGED')
 end
 
+function Module:Setup()
+  --print('|cFFFF4400'..self:GetName()..':Setup()')
+  for mapID, mapName in pairs(WORLD_QUEST_MAPS) do
+    db.QuestsByZone[mapID] = {}
+  end
+  for target, arg in pairs(callbacks) do
+    --print(type(target))
+    if type(target) == 'table' then
+      local callerName = target:GetName() or tostring(target)
+      for name, method  in pairs(arg) do
+        --print(callerName, arg)
+        hooksecurefunc(target, name, function(...)
+          self:OnSecureHook(callerName .. '.' .. name, method, ...)
+        end)
+      end
+    else
+      hooksecurefunc(target, function(...)
+        self:OnSecureHook(target, arg, ...)
+      end)
+    end
+  end
+
+
+  self:SetAllPoints(WorldMapFrame.UIElementsFrame)
+  self:UpdateArtifactPower()
+  self:UpdateBounties('SETUP')
+  self:Show()
+end
+
+function Module:OnMapInfo(isBrokenIsle, isZoomedOut, mapAreaID, isNewMap, isMapOpen)
+  if isNewMap or self.isStale then
+    print('|cFF0088FFOnMapInfo()|r, mapAreaID =', mapAreaID,'visible =', isMapOpen, 'changed =', isNewMap)
+    layoutDirty = true
+    self:Refresh('WORLD_MAP_CHANGED')
+  end
+end
+
 function Module:OnConfigUpdate()
   --print('|cFFFFFF00OnConfigUpdate()|r')
   if db.Config.FadeWhileGrouped then
@@ -158,15 +261,6 @@
       pin:SetShown(false)
     end
   end
-
-  ToggleButton.OnShow(self.Toggle)
-end
-
-local InternalHideButton = function(button, index)
-  button:Hide()
-end
-local InternalShowButton = function(button, index)
-  button:Show()
 end
 
 function Module:OnSecureHook(callbackName, func, ...)
@@ -174,126 +268,43 @@
   func(self, ...)
 end
 
-local defaults = {}
-local REWARD_UNKNOWN = 768
-function Module:OnLoad()
-  --print('|cFFFF4400'..self:GetName()..':OnLoad()')
+function Module:UpdateModifierState()
 
-  self:SetParent(WorldMapFrame.UIElementsFrame)
-  WorldPlan:AddHandler(self, defaults)
-
-  for areaID, fileName in pairs(WORLD_QUEST_MAPS) do
-    db.QuestsByZone[areaID] = {}
-  end
-
-  -- WORLD_MAP_UPDATE and PLAYER_ENTERING_WORLD are passed down from a higher level
-  self:RegisterEvent('WORLD_QUEST_COMPLETED_BY_SPELL')
-  self:RegisterEvent('SUPER_TRACKED_QUEST_CHANGED')
-  self:RegisterEvent('SKILL_LINES_CHANGED')
-  --self:RegisterEvent('CURRENT_SPELL_CAST_CHANGED')
-  self:RegisterEvent('ARTIFACT_UPDATE')
-  self:RegisterEvent('QUEST_LOG_UPDATE')
 end
 
-local artifactKnowledgeMultiplier
-local superTrackedQuestID
-function Module:OnEvent (event, ...)
+function Module:UpdateTaskPOIs()
+  canTargetQuests = SpellCanTargetQuest()
+  for i = 1, NUM_WORLDMAP_TASK_POIS do
+    local poiFrame = _G['WorldMapFrameTaskPOI'..i]
+    if poiFrame and poiFrame.worldQuest then
+      local pin = db.QuestsByID[poiFrame.questID]
+      if pin and pin.used and canTargetQuests and IsQuestIDValidSpellTarget(pin.questID) then
+        poiFrame:Show()
+      else
+        poiFrame:Hide()
+      end
+    end
+  end
+end
+-- re-anchors and scales pins that have had either of these changed due to data loading delays
+function Module:UpdateNext()
+  --print('|cFF00FF88UpdateNext()')
+  local pin = tremove(db.UpdatedPins)
 
-  print('|cFFFFFF00OnEvent() '..event..'|r', GetTime(), ...)
-  if (event == 'QUEST_LOG_UPDATE') then
-    self:UpdateBounties(event)
-  elseif event == 'WORLD_QUEST_COMPLETED_BY_SPELL' then
-    local questID = ...
-    if questID and db.QuestsByID[questID] then
-      db.QuestsByID[questID].complete = true
-      self:ReleasePin(db.QuestsByID[questID])
-    end
-    self:Refresh(event)
-  elseif event == 'SKILL_LINES_CHANGED' or event == 'CURRENT_SPELL_CAST_CHANGED' then
-    self:Refresh(event)
-  elseif event == 'ARTIFACT_UPDATE' then
-    local akCheck = C_ArtifactUI.GetArtifactKnowledgeMultiplier()
-    if akCheck and (akCheck ~= artifactKnowledgeMultiplier) then
-      if artifactKnowledgeMultiplier then
-        --print('push artifact knowledge update', artifactKnowledgeMultiplier, 'to', akCheck)
-        for index, pin in pairs( db.QuestsByID) do
-          if pin.rewardType == REWARD_ARTIFACT_POWER then
-            db.log(pin.questID .. ' ' .. tostring(pin.title) .. ' Flagged for artifact power.')
-            pin.itemNumber = 0
-            pin.dataLoaded = nil
-          end
-        end
-      else
-
-        print('artifact knowledge multiplier is known', akCheck)
-      end
-      artifactKnowledgeMultiplier = akCheck
-    end
-  elseif event == 'SUPER_TRACKED_QUEST_CHANGED' then
-    if superTrackedQuestID and db.QuestsByID[superTrackedQuestID] then
-      db.QuestsByID[superTrackedQuestID].isStale = true
-    end
-    local newID = GetSuperTrackedQuestID()
-    if newID and db.QuestsByID[newID] then
-      db.QuestsByID[newID].isStale = true
+  -- criteria state is asserted independently
+  pin:CheckFilterRules()
+  local scaleFactor = SCALE_FACTORS[(pin.dataLoaded and not pin.filtered) and scaleConstant or 1]
+  --print(pin.title, pin.dataLoaded  and not pin.filtered, scaleFactor)
+  if pin.used then
+    pin:SetAnchor(nil, pin.x, pin.y, self.hostWidth, self.hostHeight, scaleFactor)
+    if pin:IsVisible() then
+      pin:Refresh()
+    else
+      pin.isStale = true
     end
   end
 end
 
-
-function Module:OnUpdate(sinceLast)
-
-  if self.refreshBenchMarkTicker then
-    --print(self.refreshBenchMarkTicker)
-    self.refreshBenchMarkTicker =   self.refreshBenchMarkTicker - 1
-
-    if self.refreshBenchMarkTicker == 0 then
-
-      self.refreshTime = floor((GetTime() - self.refreshBenchMark) * 1000)
-      self.debugMessage:SetText(self.refreshTime)
-      self.refreshBenchMarkTicker = nil
-    end
-  else
-    self.refreshBenchMark = GetTime()
-  end
-
-  if self.filtersDirty or self.isStale then
-    self:Refresh()
-  end
-  if #db.UpdatedPins >= 1 then
-    --print('|cFF00FF88pending update', #db.UpdatedPins)
-    self:UpdateNext()
-  end
-
-end
-
-function Module:OnMapInfo(isBrokenIsle, isZoomedOut, mapAreaID, isNewMap, isMapOpen)
-  if isNewMap or self.isStale then
-    print('|cFF0088FFOnMapInfo()|r, mapAreaID =', mapAreaID,'visible =', isMapOpen, 'changed =', isNewMap)
-    layoutDirty = true
-    self:Refresh('WORLD_MAP_CHANGED')
-  else
-
-    --print('|cFFFFFF00'..self:GetName()..':OnMapInfo()|r, mapAreaID =', mapAreaID,'visible =', isMapOpen, 'changed =', isNewMap)
-  end
-end
-
-
-function Module:UpdateTaskPOIs()
-  canTargetQuests = SpellCanTargetQuest()
-  local func = canTargetQuests and 'Show' or 'Hide'
-  for i = 1, NUM_WORLDMAP_TASK_POIS do
-    local button = _G['WorldMapFrameTaskPOI'..i]
-    if button and button.worldQuest then
-      button[func](button)
-    end
-  end
-end
-
-local bountyQuests = {}
-local bountyInfo = {}
-
-local bountyDisplayLocation, bountyLockedQuestID, selectedBountyIndex, selectedBountyQuestID
 function Module:UpdateBounties(...)
     print('|cFF00FF88BountyInfo()|r', ...)
   wipe(db.BountiesByFactionID)
@@ -320,130 +331,43 @@
   bountiesDirty = nil
 end
 
-
-local totalPins = 0
-local TQ_GetQuestLocation = C_TaskQuest.GetQuestLocation
-function Module:AcquirePin (info)
-  local questID = info.questId
-  if not questID then
-    return nil
+-- check current artifact knowledge and update pins accordingly
+function Module:UpdateArtifactPower(overrideLevel)
+  if InCombatLockdown() then
+    artifactPowerDirty = true
+    return
   end
 
-  if not QuestUtils_IsQuestWorldQuest(questID) then
-    return nil
+  print('|cFF00FF88UpdateArtifactPower()|r')
+  local _, akLevel = GetCurrencyInfo(1171)
+  if overrideLevel then
+    akLevel = overrideLevel
   end
 
-  -- if we're grabbing a pin, the filters need to be checked
-  local pin = db.QuestsByID[questID]
-  if not pin then
-    local numFree = #db.FreePins
-    if numFree >= 1 then
-      pin = tremove(db.FreePins, numFree)
-      --print('|cFF00FF00Re-using', pin:GetName())
-    else
-      totalPins = totalPins + 1
-      local name = 'WorldPlanQuestMarker' .. numOverlays
-      --print('|cFF00FF00Creating', name)
-      pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin')
-
-      pin:SetID(totalPins)
-      numOverlays = numOverlays + 1
-      --pin.iconBorder:SetVertexColor(0,0,0,1)
+  --db.print('current AK', akLevel)
+  if akLevel and (akLevel ~= artifactKnowledgeLevel) or (not artifactKnowledgeLevel) then
+    --print('new ak level', akLevel)
+    db.log('AK update ' .. tostring(artifactKnowledgeLevel) .. ' to '.. tostring(akLevel))
+    for _, pin in pairs(db.QuestsByID) do
+      if (pin.rewardType == REWARD_ARTIFACT_POWER) then
+        print(pin.title, pin.itemNumber)
+        local newAP = pin:UpdateArtifactPower()
+        if newAP then
+          pin.itemNumber = newAP
+          print(newAP)
+        else
+          pin.dataLoaded = nil
+        end
+        pin.isStale = true
+      end
     end
-    pin.questID = questID
-    pin.worldQuest = true
-    pin.throttle = pin.updateRate
-    pin.isNew = true
-    pin.currentWidth = nil
-    db.QuestsByID[questID] = pin
-    tinsert(db.UsedPins, pin)
-
+    artifactKnowledgeLevel = akLevel
   end
-
-  if pin and info then
-    pin.inProgress = info.inProgress
-    pin.floor = info.floor
-    pin.numObjectives = info.numObjectives or 0
-    if info.x and info.y then
-      if (info.x ~= pin.x) or (info.y ~= pin.y) then
-        pin.isStale = true
-        --rprint('|cFFFF4400SetCoords|r', info.x, info.y)
-      end
-
-    end
-  end
-
-  pin.x = info.x or pin.x
-  pin.y = info.y or pin.y
-
-  if not HaveQuestData(questID) then
-    TQ_RequestPreloadRewardData(questID);
-  end
-
-
-  if (not pin.dataLoaded) then
-    local dataLoaded = pin:GetData()
-    if dataLoaded then
-      WorldPlan.dataFlush = true
-    else
-      isDataLoaded = false
-    end
-  end
-
-
-  pin.isActive = TQ_IsActive(questID)
-  pin:GetCriteriaState()
-  pin:CheckFilterRules()
-  --rprint(pin:GetID(), pin.filtered, pin.used)
-
-  return pin
-end
-
--- remove from index and add it to the recycling heap
-function Module:ReleasePin (pin)
-
-  local id = pin.questID
-  if id then
-    db.QuestsByID[id] = nil
-
-    for i, zone in pairs(db.QuestsByZone) do
-      --print('-', i, zone[i])
-      zone[id] = nil
-    end
-    db.TasksByID[id] = nil
-  end
-  pin:Reset()
-  tinsert(db.FreePins, pin)
-
-  WorldPlan.dataFlush = true
-  --print('|cFF00FF00-'.. (pin.mapID and GetMapNameByID(pin.mapID) or '???') ..'|r', id, pin.title)
-end
-
--- re-anchors and scales pins that have had either of these changed due to data loading delays
-function Module:UpdateNext()
-  --print('|cFF00FF88UpdateNext()')
-  local pin = tremove(db.UpdatedPins)
-    pin:CheckFilterRules()
-
-  local scaleFactor = SCALE_FACTORS[(pin.dataLoaded and not pin.filtered) and scaleConstant or 1]
-  --print(pin.title, pin.dataLoaded  and not pin.filtered, scaleFactor)
-  if pin.used then
-    pin:SetAnchor(nil, pin.x, pin.y, self.hostWidth, self.hostHeight, scaleFactor)
-    pin:OnShow()
-  end
-
-
-end
-
-function Module:Debug(...)
-  print(...)
+  artifactPowerDirty = nil
 end
 
 local msg = '|cFF00FF88WorldQuests:Refresh()|r|cFF00FFFF'
-local lastRefresh
 function Module:Refresh(...)
-  --
-
   if not self:IsVisible() then
     print('|cFFFF4400Refresh()|r', ...)
     --layoutDirty = true
@@ -482,7 +406,9 @@
   end
   --]]
 
-
+  if artifactPowerDirty and not InCombatLockdown() then
+    self:UpdateArtifactPower()
+  end
   -- calculate quests shown
   numShown = 0
   numLoaded = 0
@@ -510,19 +436,16 @@
       pin.hideReason = "Not used in map area " .. (db.currentMapID)
       pin:SetShown(false)
     end
+
   end
-
-
   --print('flags ', layoutDirty, self.isStale)
   --print(' ', numShown, 'shown,', numLoaded, 'with data')
-  if numShown > numLoaded then
-    self.Status:Show()
-  end
+
 
 --
-    self.refreshBenchMark = GetTime()
-    self.refreshBenchMarkTicker = 2
-    print('starting bench', self.refreshBenchMark)
+  self.refreshBenchMark = GetTime()
+  self.refreshBenchMarkTicker = 2
+  print('starting bench', self.refreshBenchMark)
 
 --
 
@@ -531,9 +454,13 @@
   self.sizesDirty = nil
   self.isZoomDirty = nil
 
+  if WorldPlanSummary then
+    WorldPlanSummary.isStale = true
+  end
+
+  WorldPlan.dataFlush = true
 end
 
-local refreshReason
 function Module:RefreshIfChanged(event)
   local scaleCheck = WorldMapDetailFrame:GetScale()
   refreshReason = nil
@@ -548,7 +475,6 @@
     return
   end
 
-
   if self:IsVisible() then
     print('|cFF00FFFFRefreshIfChanged()|r', refreshReason)
     self:Refresh(event)
@@ -558,7 +484,7 @@
   end
 end
 
--- update visibility states of all pins
+-- marks a pin set for passive update
 function Module:MarkAllPins(pins)
   --print('  |cFFFFFF00'..self:GetName()..':MarkAllPins()|r', pins)
   pins = pins or db.QuestsByID
@@ -568,44 +494,7 @@
   end
 end
 
--- Updates quest markers in taskInfo while associating them with the given map
-function Module:UpdateQuestsForMap(taskInfo, mapID)
-  print('|cFF00FF00UpdateQuestsForMap()|r', GetMapNameByID(mapID), GetMapNameByID(db.currentMapID), layoutDirty)
-  if db.QuestsByZone[mapID] then
-    wipe(db.QuestsByZone[mapID])
-  end
-
-
-  for index, info in pairs(taskInfo) do
-
-    local questID, x, y = info.questId, info.x, info.y
-    local pin = self:AcquirePin(info)
-
-    if pin then
-      pin.used = true
-      print(pin.title, pin.isStale, layoutDirty, (pin.owningFrame ~= WorldMapFrame))
-      if pin:IsShown() and (layoutDirty or pin.isStale or (pin.owningFrame ~= WorldMapFrame)) then
-        local scaleFactor = SCALE_FACTORS[(pin.dataLoaded and not pin.filtered) and scaleConstant or 1]
-        pin.owningFrame = WorldMapFrame
-        pin:SetAnchor(WorldMapPOIFrame, x, y, self.hostWidth, self.hostHeight, scaleFactor)
-        if pin.isStale then
-          pin:Refresh('WORLDMAP_REFRESH ' .. GetTime())
-        end
-      else
-        if layoutDirty then
-          pin.isStale = true
-        end
-      end
-
-      if db.QuestsByZone[mapID] and pin.used then
-        db.QuestsByZone[mapID][questID] = pin
-      end
-
-    end
-  end
-end
-
--- Used to refresh the visible quest markers
+-- Walks the current map tree and fires updates as needed
 function Module:UpdateAnchors ()
   wipe(self.UsedPositions)
   print('  |cFF00FF00'..self:GetName()..':UpdateAnchors()')
@@ -616,7 +505,6 @@
     layoutDirty = true
   end
 
-
   --rprint('|cFF00FF00'..self:GetName()..':UpdateAnchors()')
   local mapFileName, textureHeight, textureWidth, isMicroDungeon, microDungeonMapName = GetMapInfo()
   if isMicroDungeon then
@@ -643,11 +531,112 @@
   end
 end
 
-function ToggleButton:OnShow()
-  self:SetChecked(db.Config.EnablePins and true or false)
+-- Applies map association to the pins corresponding with each TaskInfo item
+function Module:UpdateQuestsForMap(taskInfo, mapID)
+  print('|cFF00FF00UpdateQuestsForMap()|r', GetMapNameByID(mapID), GetMapNameByID(db.currentMapID), layoutDirty)
+  if db.QuestsByZone[mapID] then
+    wipe(db.QuestsByZone[mapID])
+  elseif db.isBrokenIsle then
+    continentScanned = true
+  end
+
+  for index, info in pairs(taskInfo) do
+    local questID, x, y = info.questId, info.x, info.y
+    local pin = self:AcquirePin(info)
+
+    if pin then
+      pin.used = true
+      print(pin.title, pin.isStale, layoutDirty, (pin.owningFrame ~= WorldMapFrame))
+      if pin:IsShown() and (layoutDirty or pin.isStale or (pin.owningFrame ~= WorldMapFrame)) then
+        local scaleFactor = SCALE_FACTORS[(not pin.filtered and scaleConstant) or 1]
+        pin.owningFrame = WorldMapFrame
+        pin:SetAnchor(WorldMapPOIFrame, x, y, self.hostWidth, self.hostHeight, scaleFactor)
+        if pin.isStale then
+          pin:Refresh('WORLDMAP_REFRESH ' .. GetTime())
+        end
+      else
+        if layoutDirty then
+          pin.isStale = true
+        end
+      end
+
+      if db.QuestsByZone[mapID] and pin.used then
+        db.QuestsByZone[mapID][questID] = pin
+      end
+    end
+  end
 end
-function ToggleButton:OnClick()
-  --print(self:GetChecked())
-  db.Config.EnablePins = self:GetChecked()
-  _G.WorldPlan:OnConfigUpdate()
+
+-- locates or creates a corresponding pin frame for the provided TaskInfo data
+function Module:AcquirePin (info)
+  local questID = info.questId
+  if not (questID and QuestUtils_IsQuestWorldQuest(questID)) then
+    return nil
+  end
+
+  local pin = db.QuestsByID[questID]
+  if not pin then
+    local numFree = #db.FreePins
+    if numFree >= 1 then
+      pin = tremove(db.FreePins, numFree)
+      print('|cFF00FF00Re-using', pin:GetName())
+    else
+      totalPins = totalPins + 1
+      local name = 'WorldPlanQuestMarker' .. numOverlays
+      print('|cFF00FF00Creating', name)
+      pin = CreateFrame('Frame', name, WorldMapPOIFrame, 'WorldPlanQuestPin')
+
+      pin:SetID(totalPins)
+      numOverlays = numOverlays + 1
+      --pin.iconBorder:SetVertexColor(0,0,0,1)
+    end
+    pin.questID = questID
+    pin.worldQuest = true
+    pin.throttle = pin.updateRate
+    pin.isNew = true
+    pin.currentWidth = nil
+    db.QuestsByID[questID] = pin
+    tinsert(db.UsedPins, pin)
+  end
+
+  if IsQuestComplete(questID) then
+    pin:Release()
+    return nil
+  elseif info then
+    pin.inProgress = info.inProgress
+    pin.floor = info.floor
+    pin.numObjectives = info.numObjectives or 0
+    if info.x and info.y then
+      if (info.x ~= pin.x) or (info.y ~= pin.y) then
+        pin.isStale = true
+        --rprint('|cFFFF4400SetCoords|r', info.x, info.y)
+      end
+    end
+  end
+
+  pin.x = info.x or pin.x
+  pin.y = info.y or pin.y
+
+  if not HaveQuestRewardData(questID) then
+    TQ_RequestPreloadRewardData(questID);
+  end
+
+  if (not pin.dataLoaded) then
+    local dataLoaded = pin:GetData()
+    if dataLoaded then
+      WorldPlan.dataFlush = true
+    else
+      isDataLoaded = false
+    end
+  end
+
+  pin.isActive = TQ_IsActive(questID)
+  pin:GetCriteriaState()
+  pin:CheckFilterRules()
+  --rprint(pin:GetID(), pin.filtered, pin.used)
+  return pin
+end
+
+function Module:Debug(...)
+  print(...)
 end
\ No newline at end of file
--- a/WorldQuests.xml	Tue Apr 11 00:44:22 2017 -0400
+++ b/WorldQuests.xml	Sat Apr 15 11:04:54 2017 -0400
@@ -16,48 +16,13 @@
       <Layer level="OVERLAY">
         <FontString parentKey="debugMessage" inherits="GameFontNormal">
           <Anchors>
-            <Anchor point="BOTTOMLEFT" />
+            <Anchor point="TOP" x="0" y="-8" />
 
           </Anchors>
         </FontString>
       </Layer>
     </Layers>
 
-    <Frames>
-      <CheckButton name="$parentToggle" parentKey="Toggle" >
-        <Size x="20" y="20" />
-        <Anchors>
-          <Anchor point="TOPLEFT" relativePoint="TOPLEFT" x="2" y="-6" />
-        </Anchors>
-        <Layers>
-          <Layer level="BACKGROUND">
-            <Texture setAllPoints="false">
-              <Anchors>
-                <Anchor point="TOPRIGHT" x="-1" y="-1" />
-                <Anchor point="BOTTOMLEFT" x="1" y="1" />
-              </Anchors>
-              <Color a="1" r="0" g="0" b="0" />
-            </Texture>
-          </Layer>
-        </Layers>
-
-        <ButtonText inherits="GameFontNormal" text="World Quests">
-          <Anchors>
-            <Anchor point="LEFT" relativePoint="RIGHT" x="3" />
-          </Anchors>
-        </ButtonText>
-        <NormalTexture setAllPoints="true">
-          <Color a=".25" r="0" g="0" b="0" />
-        </NormalTexture>
-        <CheckedTexture setAllPoints="false">
-          <Anchors>
-            <Anchor point="TOPRIGHT" x="-1" y="-1" />
-            <Anchor point="BOTTOMLEFT" x="1" y="1" />
-          </Anchors>
-          <Color a=".75" r="1" g="1" b="1" />
-        </CheckedTexture>
-      </CheckButton>
-    </Frames>
   </Frame>
 
   </Ui>
\ No newline at end of file