changeset 70:131d9190db6b

Curseforge migration
author Nenue
date Wed, 28 Dec 2016 16:31:15 -0500
parents b14d0611c8d9
children ca3118127e5e
files .hgtags .pkgmeta ActionTemplates.lua BindingsFrame.lua Events.lua HotKey.lua Import.lua KeyButton.lua LibKraken/LibKraken.iml LibKraken/LibKraken.lua LibKraken/LibKraken.toc LibKraken/LibKraken.xml LibKraken/LibStub/LibStub.lua SkeletonKey.iml SkeletonKey.lua SkeletonKey.toc SkeletonKey.xml SkeletonKey/ActionTemplates.lua SkeletonKey/BindingsFrame.lua SkeletonKey/Events.lua SkeletonKey/HotKey.lua SkeletonKey/Import.lua SkeletonKey/KeyButton.lua SkeletonKey/SkeletonKey.iml SkeletonKey/SkeletonKey.lua SkeletonKey/SkeletonKey.toc SkeletonKey/SkeletonKey.xml SystemBindings.lua SystemBindings.xml
diffstat 28 files changed, 3439 insertions(+), 3669 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-67db6b712bf3edf109b3dd97b33e2f8ef56ac357 v7.0.3-release
-f6dd297cb8125e87cc5e7ae694b0b76c5e95c3bd v7.0.3-1-release
-4804892802fe4ea21fc8acb7a216b96b249a6a8b v7.0.3-2-release
-73df13211b224508b8c0b21eda0614fc5eeb3476 v7.0.3-5-release
-b0e4d04d428a9a37d700e7d94f80776dafe0f48f v7.0.3-6-release
-b627b7a7a65c05becbc3cf2cf1ccef81f655e4ad v7.0.3-7-release
-ae012c9d8dc5e36f381eb6d602ff68797fe34aeb v7.0.3-8-release
-1aaec7cefc7a67805634e357fbe5d215a79d23ff v7.0.3-9-release
-0877063fd03b7b0fb9e948de3682630f6f6723c5 v7.0.3-10-release
-1aba8a6fd4a9b5200950e164b81b34ad78f58d0d v7.0.3-51-release
-81a7c71c4483df6e02bf45f08725b826c147000a v7.0.3-53-release
-9eebce04e69b6c266053f0121abc70e5f5cf42d7 v7.0.3-58-release
-43c6528604cd8cab2ffe21f90bf29c7b1038685c v7.1-68-release
--- a/.pkgmeta	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-externals:
-    SkeletonKey/libs/LibStub:
-        url: svn://svn.wowace.com/wow/libstub/mainline/trunk
-        tag: latest
-move-folders:
-    LibKraken: SkeletonKey/libs/LibKraken
-    
-package-as: SkeletonKey
-enable-nolib-creation: no
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ActionTemplates.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,635 @@
+-- SkeletonKey
+-- ActionTemplates.lua
+-- Created: 7/29/2016 9:14 PM
+-- %file-revision%
+-- Code dealing with the implementation of action hotkeys
+
+local tostring, tonumber, pairs, ipairs = tostring, tonumber, pairs, ipairs
+local unpack, SetBinding = unpack, SetBinding
+local tinsert, tContains, select, wipe =  tinsert, tContains, select, table.wipe
+local GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo = GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo
+local GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell = GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell
+local PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells = PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells
+local GetProfessions, GetProfessionInfo, GetTalentInfo = GetProfessions, GetProfessionInfo, GetTalentInfo
+local GetNumBindings, GetBinding = GetNumBindings, GetBinding
+
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) print('SK', ...) end or nop
+local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or nop
+
+local CLICK_KEYBINDER_MACRO = "CLICK SkeletonKeyMacro:"
+local CLICK_KEYBINDER_KEY = "CLICK SkeletonKeyKey:"
+local PET_BASIC_SUBTEXT = 'Basic Attack'
+local PET_SPECIAL_SUBTEXT = 'Special Ability'
+local PETACTION_SCRIPT = {
+  [PET_ACTION_MOVE_TO] = {'pet_move_to', SLASH_PET_MOVE_TO1},
+  [PET_ACTION_ATTACK] = {'pet_attack', SLASH_PET_ATTACK1},
+  [PET_ACTION_FOLLOW] = {'pet_follow', SLASH_PET_FOLLOW1},
+  [PET_ACTION_WAIT] = {'pet_stay', SLASH_PET_STAY1 },
+  [PET_MODE_AGGRESSIVE] = {'pet_aggressive', SLASH_PET_AGGRESSIVE1 },
+  [PET_MODE_DEFENSIVE] = { 'pet_defensive', SLASH_PET_DEFENSIVE1},
+  [PET_MODE_PASSIVE] = { 'pet_passive', SLASH_PET_PASSIVE1},
+  [PET_MODE_ASSIST] = {'pet_assist', SLASH_PET_ASSIST1},
+}
+local SECONDARY_PROFESSIONS = {
+  [5] = 3,
+  [7] = 4,
+  [9] = 5,
+  [10] = 6
+}
+local petSpellCache,petSubtextCache
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
+
+--kb.ChangedBindings = {}
+--kb.ActionTypes = {}
+
+local atype = kb.ActionTypes
+
+--- Caps Lock
+atype['mount'] = function(id, name)
+  if id == SUMMON_RANDOM_FAVORITE_MOUNT_SPELL then
+    return CLICK_KEYBINDER_MACRO, 'mount_random', "/script C_MountJournal.SummonByID(0)", SkeletonKeyMacro
+  else
+    return CLICK_KEYBINDER_MACRO, 'mount_'..id, "/script C_MountJournal.SummonByID("..id..")", SkeletonKeyMacro
+  end
+end
+
+atype['macro'] = function(id, name)
+  local _, _, text = GetMacroInfo(id)
+  return CLICK_KEYBINDER_MACRO, 'macro_' .. tostring(name), name, SkeletonKeyMacro
+end
+
+atype['equipset'] = function(id, name)
+    return CLICK_KEYBINDER_MACRO, 'equipset_'..tostring(name), "/script UseEquipmentSet("..tostring(id)..")", SkeletonKeyMacro
+end
+
+atype['spell'] = function(id, name)
+  local attributeName = name
+  if kb.ProfessionCache[id] then
+    attributeName = "profession_".. kb.ProfessionCache[id].dynamicIndex .. '_' .. kb.ProfessionCache[id].dynamicSubIndex
+  end
+  return CLICK_KEYBINDER_KEY, attributeName, name, SkeletonKeyKey
+end
+
+atype['petaction'] = function(_, name)
+  -- ID doesn't exist for basic commands, even though they can be picked up
+  local attributeName, attributeValue = "petaction_" .. tostring(name), "/cast "..tostring(name)
+
+  if not petSpellCache then
+    kb.UpdatePetInfo()
+  end
+  -- Compose a multi-macro for subtext abilities
+  if petSpellCache[name] then
+    attributeValue = ""
+    for spellName, enabled in pairs(petSubtextCache[petSpellCache[name]]) do
+      attributeValue = attributeValue .. "/cast " .. spellName .. "\n"
+    end
+  end
+
+  if PETACTION_SCRIPT[name] then
+    attributeName, attributeValue = unpack(PETACTION_SCRIPT[name])
+  elseif kb.PetCache.special[name] then
+    attributeName = "petaction_"..kb.PetCache.special[name][3].."_" .. tonumber(kb.PetCache.special[name][6])
+  end
+  return CLICK_KEYBINDER_MACRO, attributeName, attributeValue, SkeletonKeyMacro
+end
+
+atype['battlepet'] = function(id, name)
+  return CLICK_KEYBINDER_MACRO, 'battlepet_' .. tostring(name), SLASH_SUMMON_BATTLE_PET1 .. " " .. tostring(name), SkeletonKeyMacro
+end
+
+atype['item'] = function(id, name)
+  return CLICK_KEYBINDER_KEY, tostring(name), id, SkeletonKeyKey
+end
+
+
+--- Resolves the SecureActionButton attribute names used for the given action
+kb.RegisterAction = function(actionType, id, name)
+  assert(atype[actionType], 'Missing actionType handler for `'..tostring(actionType)..'`')
+  local target, attributeName, attributeValue, button  = atype[actionType](id, name)
+  local command = target .. attributeName
+
+  return attributeName, attributeValue, command, target, button
+end
+
+
+
+
+kb.ApplyTalentBinding = function(talentInfo, cache)
+  talentInfo.assignedKeys = talentInfo.assignedKeys or {}
+  for i , key in pairs(talentInfo.assignedKeys) do
+    local command = CLICK_KEYBINDER_KEY.. talentInfo.actionName
+    SetBinding(key, command)
+    cprint(' **', i, '->', command)
+    tinsert(cache, talentInfo)
+  end
+end
+kb.CacheTalentBinding = function(talentInfo, cache)
+
+  local spellID = talentInfo.actionID
+  cache[spellID] = cache[spellID] or {}
+  cache[spellID] = talentInfo
+  cprint(spellID, unpack(kb.TalentBindings[spellID]))
+end
+
+
+do
+  local commandActions = {}
+  local bindings = kb.bindings
+  local key, macro = SkeletonKeyKey, SkeletonKeyMacro
+  kb.LoadBinding = function( configTable)
+    if configTable.command then
+      configTable.command = configTable.command:gsub('KeyBinder', 'SkeletonKey')
+    end
+
+    local command, name, icon, actionType, actionID, macroName, macroText =
+      configTable.command, configTable.actionName, configTable.iconPath, configTable.actionType,
+      configTable.actionID, configTable.macroName, configTable.macroText
+
+
+    local indexKey = actionType..'_'..actionID
+    local actionPrefix = "*"..actionType.."-"
+    local button = SkeletonKeyKey
+    local isAvailable = true
+    local specialButtonType
+    if actionType == 'spell' then
+      if not GetSpellInfo(actionID) then
+        isAvailable = nil
+      end
+      local dynamicInfo = kb.TalentCache[name] or kb.ProfessionCache[name]
+      if dynamicInfo then
+        cprint('|cFF00FFFFDynamicInfo:|r', name)
+        for k, v in pairs(dynamicInfo) do
+          cprint(' --', k, v)
+          configTable[k] = v
+        end
+      end
+    else
+      if actionType ~= 'macro' then
+        actionPrefix = '*macrotext-'
+      end
+
+      specialButtonType = 'macro'
+    end
+
+    local attributeSuffix, attributeValue, command, target, button =  kb.RegisterAction(actionType, actionID, name)
+    local actionKey = actionPrefix .. attributeSuffix
+    cprint('|cFF00FF88LoadBinding()|r', button:GetName(), "*type-"..attributeSuffix, actionType,  '|cFFFFFF00'..actionKey, attributeValue)
+
+
+
+    if isAvailable then
+      kb.SecureAttribute(button, "*type-"..attributeSuffix, specialButtonType or actionType)
+      kb.SecureAttribute(button, actionKey, attributeValue)
+      kb.bindings[indexKey] = configTable.assignedKeys
+      commandActions[command] = kb.bindings[indexKey]
+      return command, kb.bindings[indexKey]
+    else
+      return nil
+    end
+  end
+
+  local usedSlots = {}
+  kb.ApplyBindings = function (profile)
+    cprint('|cFF0088FFApplyBindings()')
+    --cprint('binding profile', profile)
+
+    wipe(usedSlots)
+    for slot, configTable in pairs(profile.buttons) do
+
+
+      if #configTable >= 1 then
+        local command, name, icon, actionType, actionID, macroName, macroText, spellbookSlot, spellbookType = unpack(configTable)
+
+        cprint('|CFFFF4400Fixing pad entry', slot, command, name)
+        local assignedKeys = {GetBindingKey(command)}
+        for k,v in pairs(profile.bindings) do
+          if v == command then
+            tinsert(assignedKeys, k)
+          end
+        end
+
+        configTable = {
+          command = command,
+          actionType = actionType,
+          actionName = name,
+          actionID = actionID,
+          macroName = macroName,
+          macroText = macroText,
+          iconPath = icon,
+          spellbookSlot = spellbookSlot,
+          spellbookType = spellbookType,
+          assignedKeys = assignedKeys
+        }
+        kb.currentProfile.buttons[slot] = configTable
+      end
+      if not configTable.actionID then
+        configTable.actionID = configTable.actionName
+      end
+      if not configTable.iconPath then
+        print('corrected missing icon')
+        configTable.iconPath = GetSpellTexture(configTable.actionName)
+      end
+
+      usedSlots[configTable.actionName or configTable.actionID] = true
+      usedSlots[configTable.command] = true
+
+      if kb.LoadBinding(configTable) then
+        configTable.isAvailable = true
+      end
+    end
+
+    for key, command in pairs(profile.bindings) do
+      command = command:gsub('KeyBinder', 'SkeletonKey')
+      profile.bindings[key] = command
+      cprint('|cFF00FFFF'.. key .. '|r to|cFF00FF00', command)
+      SetBinding(key, command)
+
+      if commandActions[command] and not tContains(commandActions[command], key) then
+        tinsert(commandActions[command], key)
+      end
+    end
+
+    for spellName, talentInfo in pairs(profile.talents) do
+      if not usedSlots[spellName] then
+        cprint('|cFFFF4400Unslotted talent', spellName)
+        profile.talents[spellName] = nil
+      end
+    end
+    for command in pairs(profile.bound) do
+      if not usedSlots[command] then
+        cprint('|cFFFF4400Unslotted bound entry', command)
+        profile.bound[command] = nil
+        profile.commands[command] = nil
+      end
+    end
+    for command in pairs(profile.commands) do
+      if not usedSlots[command] then
+        cprint('|cFFFF4400Unslotted command entry', command)
+        profile.commands[command] = nil
+      end
+    end
+
+  end
+
+  kb.ApplyAllBindings =function ()
+    cprint('|cFF0088FFApplyAllBindings()')
+    wipe(kb.TalentBindings)
+    wipe(kb.bindings)
+    --kb:print('Loading binding profile', kb.profileName)
+
+    -- reflect action key settings
+    if GetCVarBool("ActionButtonUseKeyDown") then
+      SkeletonKeyMacro:RegisterForClicks("AnyDown")
+      SkeletonKeyKey:RegisterForClicks("AnyDown")
+    else
+      SkeletonKeyMacro:RegisterForClicks("AnyUp")
+      SkeletonKeyKey:RegisterForClicks("AnyUp")
+    end
+
+    for i, profile in ipairs(kb.orderedProfiles) do
+      kb.ApplyBindings(profile)
+    end
+    -- do this after to ensure that profession binds are properly overridden
+    kb.UpdateProfessionInfo()
+
+    SaveBindings(GetCurrentBindingSet())
+  end
+end
+
+
+kb.specInfo = {}
+kb.UpdateSpecInfo = function()
+  kb.specInfo.id = GetSpecialization()
+  kb.specInfo.globalID, kb.specInfo.name, kb.specInfo.desc, kb.specInfo.texture = GetSpecializationInfo(kb.specInfo.id)
+end
+
+kb.UpdateMacroInfo =  function()
+  print('|cFFFFFF00kb.UpdateMacroInfo()|r')
+  for index = 1, GetNumMacros() do
+    local name = GetMacroInfo(index)
+    kb.SecureAttribute(SkeletonKeyMacro, "*type-macro_"..tostring(name), 'macro')
+    kb.SecureAttribute(SkeletonKeyMacro, "*macro-macro_"..tostring(name), index)
+  end
+end
+
+kb.UpdateTalentInfo = function()
+  print('|cFFFFFF00kb.UpdateTalentInfo()|r')
+  if kb.talentsPushed then
+    return
+  end
+  wipe(kb.TalentCache)
+  for row =1, MAX_TALENT_TIERS do
+    for col = 1, NUM_TALENT_COLUMNS do
+      local talentID, talentName, icon, selected, available, spellID = GetTalentInfo(row, col, 1)
+      local talentInfo = kb.TalentCache[spellID] or {}
+      if spellID then
+        talentInfo.actionType = 'spell'
+        talentInfo.actionName = talentName
+        talentInfo.dynamicType = 'talent'
+        talentInfo.dynamicID = talentID
+        talentInfo.dynamicIndex = row
+        talentInfo.dynamicSubIndex = col
+        talentInfo.actionID = spellID
+        talentInfo.isAvailable = selected
+        kb.TalentCache[spellID] = talentInfo
+        kb.TalentCache[talentName] = talentInfo
+        kb.DynamicSpells[spellID] = talentInfo
+        kb.DynamicSpells[talentName] = talentInfo
+      end
+
+      --print('Talent ', row, col, spellID, talentName)
+    end
+  end
+
+  for row = 1, MAX_PVP_TALENT_TIERS do
+    for col = 1, MAX_PVP_TALENT_COLUMNS do
+      local id, name, icon, selected, available, spellID, unlocked = GetPvpTalentInfo(row, col, 1)
+      if spellID then
+      local talentInfo = kb.TalentCache[spellID] or {}
+        talentInfo.actionType = 'spell'
+        talentInfo.actionName = name
+        talentInfo.dynamicType = 'talent'
+        talentInfo.dynamicID = id
+        talentInfo.actionID = spellID
+        talentInfo.isAvailable = selected
+        kb.TalentCache[spellID] = talentInfo
+        kb.TalentCache[name] = talentInfo
+        kb.DynamicSpells[spellID] = talentInfo
+        kb.DynamicSpells[name] = talentInfo
+      end
+    end
+  end
+
+  kb.talentsPushed = true
+
+  kb.UpdateDynamicButtons('talent')
+end
+
+kb.UpdateProfessionInfo = function()
+  wipe(kb.ProfessionCache)
+  local profs = {GetProfessions() }
+  --print(GetProfessions())
+  local primaryNum = 0
+  for i = 1, 6 do
+    if profs[i] then
+      local profID = profs[i]
+      local profName, texture, _, _, numSpells, spellOffset = GetProfessionInfo(profID)
+      cprint(i, profID, profName, numSpells, spellOffset)
+      if not SECONDARY_PROFESSIONS[profID] then
+        primaryNum = primaryNum + 1
+      end
+      local profNum = SECONDARY_PROFESSIONS[profID] or primaryNum
+      cprint(i, profNum)
+
+
+      kb.ProfessionCache[profNum] = kb.ProfessionCache[profNum] or {}
+
+      for j = 1, numSpells do
+        local spellName, _, icon, _, _, _, spellID = GetSpellInfo(spellOffset+j, BOOKTYPE_PROFESSION)
+        cprint(j, spellName)
+        local profInfo = {
+          actionType = 'spell',
+          actionName = spellName,
+          statusText = 'Profession ' .. i,
+          actionID = spellID,
+          iconPath = icon,
+          dynamicIndex = i,
+          dynamicSubIndex = j,
+          dynamicType = 'profession',
+          spellbookOffset = (spellOffset+j),
+          spellbookType = BOOKTYPE_PROFESSION,
+          isAvailable = true,
+          -- need to check if necessary
+          uniqueID = profID,
+        }
+
+        kb.SecureAttribute(SkeletonKeyKey, "*type-profession_"..i .. '_' ..j, "spell")
+        kb.SecureAttribute(SkeletonKeyKey, "*spell-profession_"..i .. '_' ..j, spellName)
+
+        kb.ProfessionCache[spellName] = profInfo
+        kb.ProfessionCache[spellID] = profInfo
+
+        kb.DynamicSpells[spellName] = profInfo
+        kb.DynamicSpells[spellID] = profInfo
+
+        kb.DynamicSpells.profession[i] = kb.DynamicSpells.profession[i] or {}
+        kb.DynamicSpells.profession[i][j] = profInfo
+        --print('  |cFF0088FF['..i..']|r|cFFFF44BB['..spellOffset+i..']|r', spellName, "profession_"..i .. '_' ..j)
+      end
+    end
+
+  end
+
+  kb.UpdateDynamicButtons('profession')
+end
+
+
+
+kb.UpdatePetInfo = function()
+  local hasPetSpells, petType = HasPetSpells()
+
+  -- reconcile saved data if it becomes available
+  if kb.db then
+    kb.db.petSpellsDB = kb.db.petSpellsDB or {}
+    kb.db.petSpellsDB.subtext = kb.db.petSpellsDB.subtext or {}
+    kb.db.petSpellsDB.spell = kb.db.petSpellsDB.spell or {}
+    local spellCache = kb.db.petSpellsDB.spell
+    local subtextCache = kb.db.petSpellsDB.subtext
+    if petSpellCache then
+      for k,v in pairs(petSpellCache) do
+        if not spellCache[k] then
+          spellCache[k] = v
+        end
+      end
+    end
+    petSpellCache = spellCache
+    if petSubtextCache then
+      for k,v in pairs(petSubtextCache) do
+        if not subtextCache[k] then
+          subtextCache[k] = v
+        end
+      end
+    end
+    petSubtextCache = subtextCache
+  else
+    petSpellCache = {}
+    petSubtextCache = {}
+  end
+
+  if PetHasSpellbook() then
+    --print('PET SPELLBOOK')
+    local spellbookOffset = 1
+    local specialNum = {}
+    local newSubtextItems = false
+
+    repeat
+
+      local spellType, spellID = GetSpellBookItemInfo(spellbookOffset, BOOKTYPE_PET)
+      local spellName, subText = GetSpellBookItemName(spellbookOffset, BOOKTYPE_PET)
+      local texture = GetSpellBookItemTexture(spellbookOffset, BOOKTYPE_PET)
+      if (spellType == 'SPELL') and (not IsPassiveSpell(spellbookOffset, BOOKTYPE_PET)) then
+        local info = kb.PetCache[spellName] or {}
+        kb.PetCache.spellslot[spellName] = {spellbookOffset, spellName, subText, spellID, texture}
+        --print('|cFF00FF88spellslot['..spellName..']|r', '=>', i, subText)
+
+        if subText then
+          kb.PetCache.subtext[subText] = kb.PetCache.subtext[subText] or {}
+          specialNum[subText] = (specialNum[subText] or 0) + 1
+
+          petSpellCache[spellName] = subText
+          petSubtextCache[subText] = petSubtextCache[subText] or {}
+
+          -- add to the list
+          if not petSubtextCache[subText][spellName] then
+            petSubtextCache[subText][spellName] = true
+            newSubtextItems = true
+            --print('|cFF00FFFFspecial['..spellName..']|r', '\n','|cFF00FFFFsubtext['..subText..']['..specialNum[subText]..']|r', '=>', i, spellName, subText, spellID,  texture, specialNum[subText])
+          end
+
+
+
+          local entry = {spellbookOffset, spellName, subText, spellID,  texture, specialNum[subText] }
+
+
+          info.spellbookOffset = spellbookOffset
+          info.spellbookType = BOOKTYPE_PET
+          info.actionName = spellName
+          info.spellID = spellID
+          info.dynamicType = 'petaction'
+          info.dynamicID = spellID
+          info.dynamicIndex = subText
+          info.dynamicSubIndex = specialNum[subText]
+          info.isAvailable = true
+
+          kb.PetCache.special[spellName] = info
+          kb.PetCache.subtext[subText][specialNum[subText]] = info
+          kb.DynamicSpells[spellName] = info
+          kb.DynamicSpells[spellID] = info
+
+        end
+
+        if spellID then
+          kb.PetCache.spell[spellbookOffset] = {spellID, spellName, subText}
+          --print('|cFF0088FFspell['..i..']|r', '=>', spellID, spellName, subText)
+        end
+
+        kb.PetCache[spellName] = info
+      end
+
+      spellbookOffset = spellbookOffset + 1
+    until spellType == nil
+
+    if newSubtextItems then
+
+      local macrotext = ""
+      for subText, spells in pairs(petSubtextCache) do
+        if specialNum[subText] then
+          for spellName, enabled in pairs(spells) do
+            macrotext = macrotext .. "/cast " .. spellName .. "\n"
+          end
+          kb.SecureAttribute(SkeletonKeyMacro, "*macrotext-petaction_"..subText.."_"..specialNum[subText], macrotext)
+        end
+      end
+    end
+
+
+  else
+    --print('NO PET SPELLBOOK')
+    wipe(kb.PetCache.spell)
+    wipe(kb.PetCache.spellslot)
+  end
+
+  if PetHasActionBar() then
+    --print('PET ACTION BAR')
+    for i = 1, 10 do
+
+
+      local name, subtext, texture, isToken, isActive = GetPetActionInfo(i)
+      if name then
+        kb.PetCache.action[i] = {name, subtext, texture, isToken, isActive }
+
+
+      end
+      --print('|cFFFFFF00action['..i..']|r', name, subtext, texture)
+    end
+  else
+    --print('NO PET ACTION BAR')
+    wipe(kb.PetCache.action)
+  end
+
+  kb.UpdateDynamicButtons('petaction')
+
+end
+
+kb.UpdateSystemBinds = function()
+  wipe(kb.SystemBindings)
+  local n = GetNumBindings()
+  for i=1, n do
+    local command, key1, key2 = GetBinding(i)
+    if not command:match('ACTION.*%d+') then
+      if key1 then
+        kb.SystemBindings[key1] = command
+      end
+      if key2 then
+        kb.SystemBindings[key2] = command
+      end
+    else
+      --print('ignoring action button binding', command)
+    end
+  end
+end
+
+kb.UpdateDynamicButtons = function(dynamicType)
+  for i, button in ipairs(kb.buttons) do
+    if button.isDynamic == dynamicType then
+      kb.UpdateSlot(button, true)
+    end
+  end
+end
+
+kb.pendingAttributes = {}
+kb.SecureAttribute = function(target, name, value)
+  if InCombatLockdown() then
+    if #kb.pendingAttributes == 0 then
+      kb:print(kb.L('Key bindings will be applied when you exit combat.'))
+    end
+
+    tinsert(kb.pendingAttributes, {target, name, value})
+    SkeletonKey:RegisterEvent('PLAYER_REGEN_ENABLED')
+
+  else
+
+    --cprint('|cFFFF4444' .. target:GetName()..'|r.|cFFFFFF00'.. tostring(name)..'|r = "'..tostring(value)..'"')
+    target:SetAttribute(name, value)
+  end
+end
+
+kb.PLAYER_REGEN_ENABLED = function()
+  if #kb.pendingAttributes >= 1 then
+    local args = tremove(kb.pendingAttributes)
+    while args do
+      local target, name, value = unpack(args)
+      --print(target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"')
+      cprint('deferred', target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"')
+      target:SetAttribute(name, value)
+      args = tremove(kb.pendingAttributes)
+    end
+  end
+
+  if #kb.pendingCalls >= 1 then
+
+    local func = tremove(kb.pendingCalls)
+    while func do
+      func()
+    end
+  end
+end
+
+kb.UpdateBindingsCache = function(actionType, actionID, bindings)
+  local indexKey = actionType .. '_' .. actionID
+  kb.bindings[indexKey] = bindings
+
+  cprint('|cFF00FF00'..indexKey..'|r = {'.. table.concat(bindings,', ').. '}')
+  tinsert(kb.ChangedBindings, {actionType, actionID})
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BindingsFrame.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,652 @@
+-- KrakTool
+-- BindingsFrame.lua
+-- Created: 7/28/2016 3:39 PM
+-- %file-revision%
+-- Handles the arrangement of and interaction with the SkeletonKey frame
+--[=[
+  -- some useful texture paths
+  [[Interface\PaperDollInfoFrame\UI-GearManager-Undo]]
+  [[Interface\PetPaperDollFrame\UI-PetHappiness]]
+  [[Interface\RAIDFRAME\ReadyCheck-Waiting]]
+  [[Interface\RAIDFRAME\ReadyCheck-Read]]
+  [[Interface\RAIDFRAME\ReadyCheck-NotReady]]
+  [[Interface\TradeSkillFrame\UI-TradeSkill-LinkButton]]
+  [[Interface\TUTORIALFRAME\UI-TUTORIAL-FRAME]]
+  [[Interface\UI-TutorialFrame-QuestGiver\UI-TutorialFrame-QuestGray]]
+--]=]
+
+SkeletonKeyButtonMixin = {}
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('SK', ...) end or nop
+local L = kb.L
+local BINDS_PER_ROW = 2
+local BINDING_TYPE_SPECIALIZATION = 3
+local BINDING_TYPE_CHARACTER = 2
+local BINDING_TYPE_GLOBAL = 1
+local BUTTON_HSPACING = 128
+local BUTTON_SPACING = 4
+local BUTTON_PADDING = 12
+local TAB_HEIGHT = 24
+local KEY_BUTTON_SIZE = 48
+local NUM_KEY_SLOTS = BINDS_PER_ROW * 8
+local TAB_HEIGHT = 40
+local BG_INSET = 4
+
+local BINDING_SCHEME_COLOR = {
+  [BINDING_TYPE_GLOBAL] = {0,.125,.5,.8},
+  [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.8},
+  [BINDING_TYPE_SPECIALIZATION] = {.25,0,0,0.8},
+}
+local BINDING_SCHEME_VERTEX = {
+  [BINDING_TYPE_GLOBAL] = {0,.5,1,1},
+  [BINDING_TYPE_CHARACTER] = {0,1,0,1},
+  [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1},
+}
+local BINDING_SCHEME_TEXT = {
+  [BINDING_TYPE_SPECIALIZATION] = {0, 1, 1},
+  [BINDING_TYPE_CHARACTER] = {0, 1, 0},
+  [BINDING_TYPE_GLOBAL] = {0, 1, 1}
+}
+
+local match, strupper = string.match, string.upper
+local tremove, tinsert, ipairs, pairs, unpack = table.remove, table.insert, ipairs, pairs, unpack
+local tonumber, tostring = tonumber, tostring
+local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
+local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
+local GetBindingAction, GetBindingKey, GetCurrentBindingSet = GetBindingAction, GetBindingKey, GetCurrentBindingSet
+local SetBinding, SaveBindings = SetBinding, SaveBindings
+local GetSpellInfo, InCombatLockdown = GetSpellInfo, InCombatLockdown
+
+
+local ActionListPanel = {
+  tabButtons = {
+    [BINDING_TYPE_GLOBAL] = {
+      icon = "Interface\\WORLDMAP\\WorldMap-Icon",
+      label = "Global",
+    },
+    [BINDING_TYPE_CHARACTER] ={
+      func = function(self, index)
+        SetPortraitTexture(self.Icon, 'player')
+        self.Label:SetText(kb.configHeaders[index])
+        self.tooltipText = kb.configHeaders[index]
+      end
+    },
+    [BINDING_TYPE_SPECIALIZATION] = {
+      func = function(self, index)
+        self.Icon:SetTexture(kb.specInfo.texture)
+        self.Label:SetText(kb.configHeaders[index])
+        self.tooltipText = kb.configHeaders[index]
+      end
+    },
+  }
+}
+local SystemBindingsPanel = {
+  tabButtons = {
+    {label = "Global"},
+    {label = "Character"}
+  }
+}
+function SkeletonKeyMixin:ProcessInput (key)
+  if self.currentPanel then
+    if self.currentPanel:OnInput(key) then
+      self:Update(true)
+    end
+  end
+end
+
+local lastFolder
+local restingAlpha = 0.7
+local fadeTime, fadeDelay = .30, 0.15
+local saveButton
+
+
+
+local frameCount = 0
+local lastCheckFrame
+local KeyBinder_CheckButton = function(frame ,enableText, disableText, dbKey, tooltipText, callback, header)
+  if kb.db[dbKey] then
+    frame:SetChecked(true)
+  end
+
+  frame.header:SetText(header)
+
+  frame:SetScript('OnClick', function(self)
+    kb.db[dbKey] = self:GetChecked()
+    if callback then
+      callback(self)
+    end
+    kb.ui()
+  end)
+
+  frame:SetScript('OnEnter', function(self)
+    if tooltipText then
+      GameTooltip:SetOwner(self)
+      GameTooltip:SetText(tooltipText)
+      GameTooltip:Show()
+    end
+  end)
+
+  frame:SetScript('OnLeave', function(self)
+    if tooltipText and GameTooltip:GetOwner() == self then
+      GameTooltip:Hide()
+    end
+  end)
+
+  if frame:GetID() == 0 then
+    frameCount = frameCount + 1
+    frame:SetID(frameCount)
+    print('checkbutton #', frameCount)
+    if frameCount == 1 then
+      frame:ClearAllPoints()
+      frame:SetPoint('TOP', KeyBinderInventoryButton, 'BOTTOM', 0, -22)
+      frame:SetPoint('LEFT', SkeletonKey 'LEFT', 2, 0)
+    else
+      frame:ClearAllPoints()
+      frame:SetPoint('TOPLEFT', lastCheckFrame, 'BOTTOMLEFT', 0, -2)
+    end
+
+    frame.header:ClearAllPoints()
+    frame.header:SetPoint('LEFT', frame, 'RIGHT', 2, 0)
+
+    lastCheckFrame = frame
+  end
+end
+
+
+
+function SkeletonKeyMixin:OnMouseWheel(delta)
+
+  -- let the updaters handle range
+  if IsControlKeyDown() then
+    self.zoomScale = self.zoomScale - (delta/10)
+  else
+    self.scrollOffset = ceil(self.scrollOffset - delta)
+  end
+
+  self:Update(true)
+  print(self.zoomScale, self.scrollOffset)
+end
+
+function SkeletonKeyMixin:OnHide()
+  KeyBinderImportLog:Hide()
+end
+
+
+local tabID = 0
+local prevTab
+
+function SkeletonKeyMixin:SetupTabButton (index, text, icon, func)
+  print('|cFF00FFFF'..self:GetName()..':SetupTabButton()', index, text, icon, func)
+  local tabName = 'SkeletonKeyProfileTab'..index
+  local tab = _G[tabName]
+
+  if not tab then
+    tab = CreateFrame('Button', tabName, self, 'SkeletonKeyTabTemplate')
+    self.numTabs = self.numTabs + 1
+    tab:SetID(self.numTabs)
+    TAB_HEIGHT = tab:GetHeight()
+
+    if self.numTabs == 1 then
+      tab:SetPoint('TOPLEFT', self.profilebg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING)
+    else
+      tab:SetPoint('TOPLEFT', self.lastTab,'TOPRIGHT', BUTTON_SPACING, 0)
+    end
+
+    tab.tooltipText = text
+    tab:SetScript('OnEnter', function(button)
+      if button.tooltipText then
+        GameTooltip:SetOwner(button)
+        GameTooltip:SetText(button.tooltipText)
+        GameTooltip:Show()
+      end
+    end)
+
+    tab:SetScript('OnLeave', function(button)
+      if GameTooltip:IsOwned(button) then
+        GameTooltip:Hide()
+      end
+    end)
+
+    tab:SetScript('OnClick', function(button)
+      self.selectedTabIndex = button:GetID()
+      self:Update(true)
+    end)
+    self.lastTab = tab
+  end
+  if text then
+
+    tab.Label:SetText(text)
+  end
+
+  if icon then
+    tab.Icon:SetTexture(icon)
+  end
+  if func then
+    func(tab, index, text)
+  end
+
+  local selected = (index == self.selectedTabIndex)
+  if selected then
+    tab.Icon:SetDesaturated(false)
+    tab.Label:SetTextColor(0,1,0, 1)
+  else
+
+    tab.Icon:SetDesaturated(true)
+    tab.Label:SetTextColor(1,1,1,0.7)
+  end
+
+  tab.used = true
+
+  tab:SetSize(tab.Icon:GetWidth()+tab.Label:GetStringWidth()+3, tab.Icon:GetHeight())
+  tab:Show()
+  print(tab:GetPoint(1))
+  print(tab:GetSize())
+
+  return tab
+end
+
+
+
+--- push current information into living UI
+function SkeletonKeyMixin:Update(force)
+  print('|cFFFF8800'..self:GetName()..':Update()|r', InCombatLockdown() and 'combat', self:IsShown())
+  for index, frame in ipairs(self.Plugins) do
+    if frame.Update then
+      frame:Update(force)
+    end
+  end
+
+  self.currentPanel = self.currentPanel or self.Panels[1]
+  if InCombatLockdown() or not self:IsShown() then
+    return
+  end
+
+  self.numTabs = 0
+  for index, tab in ipairs(self.tabButtons) do
+    tab.used = nil
+    tab:Hide()
+  end
+
+  for index, panel in ipairs(self.Panels) do
+    print(panel:GetName())
+    if panel == self.currentPanel then
+      print('Updating panel:', panel:GetName())
+      panel:SetAllPoints(self.bg)
+      self.selectedTabIndex, self.scrollOffset = panel:Update(force)
+      panel:Show()
+
+      for tabIndex, info in ipairs(panel.tabButtons) do
+        self:SetupTabButton(tabIndex, info.label, info.icon, info.func)
+      end
+
+    else
+      panel:Hide()
+    end
+  end
+
+
+
+  --- Frame Sizing
+  self.profilebg:SetHeight(TAB_HEIGHT + BUTTON_PADDING * 2 + self.profiletext:GetStringHeight())
+
+  self.bg:SetWidth((KEY_BUTTON_SIZE + BUTTON_HSPACING + BUTTON_SPACING) * BINDS_PER_ROW + BUTTON_PADDING*2 - BUTTON_SPACING - BG_INSET*2)
+  local numRows = NUM_KEY_SLOTS/BINDS_PER_ROW
+
+  self.bg:SetHeight((KEY_BUTTON_SIZE + BUTTON_SPACING) * numRows + BUTTON_PADDING*2 - BUTTON_SPACING - BG_INSET*2)
+
+
+  self:SetHeight(self.headerbg:GetHeight() + self.profilebg:GetHeight() + self.bg:GetHeight() + self.footer:GetHeight()+BG_INSET*2)
+  self:SetWidth(((BINDS_PER_ROW * (KEY_BUTTON_SIZE + BUTTON_HSPACING) + (BINDS_PER_ROW - 1) * BUTTON_SPACING + BUTTON_PADDING * 2) ))
+
+
+  self.backdrop.insets.left = BG_INSET
+  self.backdrop.insets.right = BG_INSET
+  self.backdrop.insets.top = BG_INSET
+  self.backdrop.insets.bottom = BG_INSET
+  self:SetBackdrop(self.backdrop)
+  self:SetBackdropColor(unpack(self.backdropColor))
+  self:SetBackdropBorderColor(unpack(self.backdropBorder))
+
+  self:SetScale(self.zoomScale)
+
+  self.profiletext:SetText(kb.configHeaders[kb.db.bindMode])
+  print(kb.db.bindMode, kb.configHeaders[kb.db.bindMode], self:GetSize())
+  print(self:GetPoint(1))
+
+
+  self:EnableKeyboard((kb.saveTarget and true) or false)
+  print('keyboard input:', (kb.saveTarget and true) or false)
+
+  -- Reset this so talent cache can be rebuilt
+  kb.talentsPushed = nil
+end
+
+local SkeletonKeyPanel = {}
+function SkeletonKeyPanel:OnShow()
+  print('|cFFFFFF00'..self:GetName()..':OnShow()|r')
+end
+
+function ActionListPanel:OnLoad()
+
+
+  self.UnbindButton:SetScript('OnClick', function()
+    self:UnbindSlot(kb.saveTarget)
+    SkeletonKey:Update()
+  end)
+end
+
+function ActionListPanel:Update(force)
+  local parent = self:GetParent()
+  local tabID = parent.selectedTabIndex
+  local scrollOffset = parent.scrollOffset
+  if not tabID then
+    tabID = kb.db.bindMode or BINDING_TYPE_GLOBAL
+  end
+  print('|cFF0088FF'..self:GetName()..':Update()|r', 'tab', parent.selectedTabIndex, 'scroll', parent.scrollOffset)
+
+  local selectedProfile = kb.loadedProfiles[tabID]
+  if selectedProfile then
+    kb.currentProfile = selectedProfile
+    kb.db.bindMode = tabID
+  else
+    tabID = BINDING_TYPE_GLOBAL
+  end
+  scrollOffset = scrollOffset or 0
+
+  local leftSlot, upSlot
+  local buttonTable = self.buttons or {}
+  for index = 1, NUM_KEY_SLOTS do
+    if not buttonTable[index] then
+      local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, self, 'KeyButton')
+      local newRow = (mod(index, BINDS_PER_ROW) == 1)
+
+      if index == 1 then
+        button:SetPoint('TOPLEFT', self, 'TOPLEFT', BUTTON_PADDING, - BUTTON_PADDING)
+        upSlot = button
+      elseif newRow then
+        button:SetPoint('TOPLEFT', upSlot, 'BOTTOMLEFT', 0, -BUTTON_SPACING)
+        upSlot = button
+      else
+        button:SetPoint('TOPLEFT', leftSlot, 'TOPRIGHT', BUTTON_HSPACING, 0)
+      end
+
+      button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE)
+      button:Show()
+      buttonTable[index] = button
+      leftSlot = button
+    end
+  end
+  self.buttons = buttonTable
+
+  local startIndex = scrollOffset * BINDS_PER_ROW
+  for i, button in ipairs(self.buttons) do
+    button:SetID(startIndex+i)
+    button:UpdateSlot(force)
+    button:SetFrameLevel(50 + i + (button.isActive and #self.buttons or 0))
+  end
+
+
+  local r,g,b,a = unpack(BINDING_SCHEME_COLOR[kb.db.bindMode])
+  self.profileStripe:SetColorTexture(r,g,b)
+  if kb.saveTarget then
+    self.bg:SetColorTexture(.2,.5, .2, .5)
+    self.UnbindButton:SetFrameLevel(kb.saveTarget:GetFrameLevel()-1)
+    self.UnbindButton:SetPoint('TOPLEFT', kb.saveTarget, 'BOTTOMLEFT', 0, -1)
+    self.UnbindButton:Show()
+
+  else
+    self.bg:SetColorTexture(.2,.2,.2,1)
+    self.UnbindButton:Hide()
+  end
+
+  return tabID, scrollOffset
+end
+
+
+function ActionListPanel:ActivateSlot (button)
+  if kb.saveTarget then
+    kb.saveTarget.isActive = nil
+  end
+  button.isActive = true
+  kb.saveTarget = button
+  return true
+end
+
+function ActionListPanel:DeactivateSlot (button)
+  button.isActive = nil
+  kb.saveTarget = nil
+  return true
+end
+
+function ActionListPanel:OnInput(key)
+
+  if key == 'ESCAPE' then
+    return self:DeactivateSlot(kb.saveTarget)
+  end
+
+  if (match(key, '[RL]SHIFT') or match(key, '[RL]ALT') or match(key, '[RL]CTRL')) then
+    return
+  end
+
+  if kb.saveTarget then
+    if kb.saveTarget:SaveSlot(key) then
+      if not (kb.db.stickyMode or kb.db.hoverInput) then
+        return self:DeactivateSlot(kb.saveTarget)
+      end
+      return true
+    end
+  end
+end
+
+
+function SystemBindingsPanel:Update(force)
+end
+
+--- Associate processed input with the given slot's metadata
+function SkeletonKeyButtonMixin:SaveSlot (key)
+
+  if not self.command then
+    return
+  end
+  if InCombatLockdown() then
+    kb:print(L('Bindings cannot be changed during combat.'))
+    return
+  end
+
+  local spellName = self.actionName
+
+  print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
+
+  local modifier = ''
+  if IsAltKeyDown() then
+    modifier = 'ALT-'
+  end
+  if IsControlKeyDown() then
+    modifier = modifier.. 'CTRL-'
+  end
+  if IsShiftKeyDown() then
+    modifier = modifier..'SHIFT-'
+  end
+  local binding = modifier..key
+
+  -- check for system bindings
+  --bprint('|cFFFFFF00SaveBind|r', 'protectKeys', kb.db.protectBlizKeys)
+  if kb.db.protectBlizKeys and kb.SystemBindings[binding] then
+    kb:print(L('BINDING_FAILED_PROTECTED', binding, kb.SystemBindings[binding]))
+    return false
+  end
+
+  -- check for other keys
+  local previousCommand = GetBindingAction(binding)
+  if previousCommand ~= "" and previousCommand ~= self.command then
+    local actionType, actionID, name = kb.GetCommandAction(previousCommand)
+    if actionType then
+      local keys = {GetBindingKey(previousCommand) }
+      local  i = 1
+      while keys[i] do
+        if keys[i] == binding then
+          tremove(keys, i)
+          kb.UpdateBindingsCache(actionType, actionID, keys)
+          break
+        end
+        i = i + 1
+      end
+    end
+  end
+
+
+  if self.isAvailable then
+    print('Binding available spell', binding, self.command)
+    SetBinding(binding, self.command)
+    SaveBindings(GetCurrentBindingSet())
+    self.assignedKeys = {GetBindingKey(self.command) }
+
+    kb:print(L('BINDING_ASSIGNED', binding, self.actionName, kb.currentHeader))
+  else
+    kb:print(L('UNSELECTED_TALENT_ASSIGNED', binding, self.actionName, kb.currentHeader))
+  end
+
+  if not tContains(self.assignedKeys, binding) then
+    tinsert(self.assignedKeys, 1, binding)
+  end
+
+  local talentInfo
+  if spellName and kb.TalentCache[spellName] then
+    print('store dynamicType talent')
+    talentInfo = {
+      macroName = self.macroName,
+      actionName = self.actionName,
+      actionType = self.actionType,
+      actionID = self.actionID,
+      assignedKeys = self.assignedKeys
+    }
+    kb.currentProfile.talents[spellName] = talentInfo
+  end
+
+  for _, key in ipairs(self.assignedKeys) do
+    if not kb.currentProfile.bindings[key] then
+      kb.currentProfile.bindings[key] = self.command
+    end
+  end
+
+  for level, profile in ipairs(kb.orderedProfiles) do
+    if (level > kb.db.bindMode) then
+      profile.bindings[binding] = nil
+      profile.commands[self.command] = nil
+      profile.bound[self.command] = nil
+      if spellName then
+        profile.talents[spellName] = nil
+      end
+    end
+  end
+
+  kb.UpdateBindingsCache(self.actionType, self.actionID, self.assignedKeys)
+
+  self.binding = binding
+
+  return true
+end
+
+function SkeletonKeyMixin:OnKeyDown(key)
+  self:ProcessInput(key)
+end
+function SkeletonKeyMixin:OnKeyUp(key)
+end
+
+function SkeletonKeyMixin:OnDragStart()
+  self:StartMoving()
+end
+function SkeletonKeyMixin:OnDragStop()
+  self:StopMovingOrSizing()
+end
+
+function ActionListPanel:UnbindSlot (button)
+
+  local button = button or kb.saveTarget
+  if not button then
+    return
+  end
+
+  local command = button.command
+  local actionType = button.actionType
+  local actionID = button.actionID
+
+  local keys = {GetBindingKey(command) }
+  if #keys >= 1 then
+    kb.UpdateBindingsCache(actionType, actionID, {})
+  end
+
+  local talentName = button.actionName
+  if actionType == 'macro' then
+    local spellName, _, spellID = GetMacroSpell(actionID)
+    talentName = spellName
+  end
+
+
+  --print('detected', #keys, 'bindings')
+  for i, key in pairs(keys) do
+    --print('clearing', key)
+    SetBinding(key, nil)
+    SaveBindings(GetCurrentBindingSet())
+    if kb.currentProfile.bindings[key] then
+      --kb:print(L('BINDING_REMOVED', self.actionName, kb.currentHeader))
+      kb.currentProfile.bindings[key] = nil
+    end
+    if kb.currentProfile.talents[talentName] then
+      kb.currentProfile.talents[talentName] = nil
+    end
+
+    kb.bindings[tostring(actionType)..'_'..tostring(actionID)] = nil
+  end
+  if kb.currentProfile.bound[command] then
+    kb.currentProfile.bound[command] = nil
+    --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[db.bindMode]))
+  end
+  kb.saveTarget = nil
+
+  return true
+end
+
+kb.AcceptAssignment = function(self, ...)
+  local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
+  local source = kb.  loadedProfiles[popup.oldProfile]
+  popup.slot:SetSlot(unpack(popup.args))
+  popup.slot:UpdateSlot()
+  --kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) -- re-enable scrolling
+  ClearCursor()
+  ResetCursor()
+end
+
+--- Add to blizzard interfaces
+StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] = {
+  text = "Confirm moving an assigned command.",
+  button1 = OKAY,
+  button2 = CANCEL,
+  timeout = 0,
+  whileDead = 1,
+  showAlert = 1,
+  OnAccept = kb.AcceptAssignment,
+  --OnCancel = function() kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) end
+}
+
+
+
+do
+  local MACRO_SELECTED_ID = 1
+  local MACRO_SELECTED_NAME = GetMacroInfo(1)
+  kb.CreateMacroHooks = function()
+    print('|cFF00FF00setting up MacroUI hooks')
+    hooksecurefunc("MacroFrame_SelectMacro", function(id)
+      print('|cFF0088FFMacroFrame_SelectMacro|r', id)
+      MACRO_SELECTED_ID = id
+      for k,v in pairs(kb.bindings) do
+        --print(k,v)
+      end
+
+    end)
+  end
+end
+
+
+SkeletonKeyActionListMixin = Mixin(ActionListPanel, SkeletonKeyPanel)
+SkeletonKeySystemBindingsMixin = Mixin(SystemBindingsPanel, SkeletonKeyPanel)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Events.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,60 @@
+-- KrakTool
+-- Events.lua
+-- Created: 7/24/2016 11:10 PM
+-- %file-revision%
+-- Event handlers, and the init block that sets them up; nothing else should be here
+
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('SkeletonKey', ...) end or function() end
+
+
+kb.ADDON_LOADED = function(_, _, addon)
+
+  if addon == 'Blizzard_MacroUI' then
+    kb.CreateMacroHooks()
+  end
+end
+
+kb.PLAYER_REGEN_DISABLED = function()
+  SkeletonKey:SetShown(false)
+end
+
+kb.UNIT_PORTRAIT_UPDATE = function()
+  SkeletonKey:Update()
+end
+
+kb.PLAYER_REGEN_ENABLED = function()
+  SkeletonKey:Update()
+end
+
+kb.PLAYER_SPECIALIZATION_CHANGED =  function(...)
+  kb.UpdateSpecInfo()
+  kb.UpdateTalentInfo()
+  kb.SelectProfileSet(kb.profileName)
+  kb.ApplyAllBindings()
+  SkeletonKey:Update(true)
+end
+kb.PLAYER_TALENT_UPDATE = function()
+  kb.UpdateTalentInfo()
+  kb.SelectProfileSet(kb.profileName)
+  kb.ApplyAllBindings()
+  SkeletonKey:Update()
+end
+kb.ACTIONBAR_SLOT_CHANGED = function(self, event, slot)
+  --kb.HotKeyText(slot)
+  return true
+end
+
+-- only need to respond to this for pet actions
+kb.SPELLS_CHANGED = function(self, event, unit)
+  print('|cFFFF0088'.. event..'|r', unit)
+  kb.UpdatePetInfo()
+end
+
+kb.UPDATE_MACROS = function()
+  kb.UpdateMacroInfo()
+end
+
+kb.UPDATE_BINDINGS = function()
+  kb.UpdateSystemBinds()
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/HotKey.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,303 @@
+-- KrakTool
+-- HotKey.lua
+-- Created: 7/22/2016 10:28 PM
+-- %file-revision%
+-- Module for fixing actionbar hotkey text
+
+SkeletonHotKeyMixin = {}
+local _G, wipe, tContains, tinsert = _G, table.wipe, tContains, tinsert
+local hotkeyText = {}
+local blizHotKey = {}
+local bindings
+
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('HotKey', ...) end or nop
+local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or nop
+
+-- frames obtained via post-load hooks, created by addons like Dominos or BarTender4
+local loadedFrames = {}
+-- frames divided by update categories
+local categoryFrames = {}
+-- frames indexed by action slot ID (just the action bar, for... reasons)
+local actionSlots = {}
+local actionFrames = {}
+local actionIndex = {}
+local hotkey = SkeletonHotKeyMixin
+
+--- Used to determine which groups of action buttons need updating
+local hotkeyEvents = {
+  ["UPDATE_BONUS_ACTIONBAR"] = {"bonus"},
+  ["UPDATE_VEHICLE_ACTIONBAR"] = {"vehicle"},
+  ["UPDATE_OVERRIDE_ACTIONBAR"] = {"override"},
+  ["ACTIONBAR_PAGE_CHANGED"] = {"actionbar"},
+  ["ACTIONBAR_SLOT_CHANGED"] = {"actionslot"},
+  ["PLAYER_ENTERING_WORLD"] = {"world","all"},
+  ["PET_UI_UPDATE"] = {"pet"},
+  ["PLAYER_SPECIALIZATION_CHANGED"] = {"player"},
+  ["PLAYER_TALENTS_UPDATED"] = {"player"},
+}
+
+
+function hotkey:wrapEvent (event, ...)
+  self:RegisterEvent(event)
+  hotkeyEvents[event] = {...}
+  self[event] = self.UpdateFromEvent
+end
+
+function hotkey:unwrapEvent (event)
+  if not self[event] then
+    self:UnregisterEvent(event)
+  end
+  hotkeyEvents[event] = nil
+  self[event] = nil
+end
+
+
+hotkey.RegisterFrame = function(frame)
+  --wrap(function()
+  print('ActionBarButtonEventsFrame_RegisterFrame(', frame:GetName(), frame.action, frame:IsVisible(), frame:IsShown())
+  --end)
+  blizHotKey[frame] = frame.HotKey
+  loadedFrames[frame] = true
+end
+
+function hotkey:OnEvent (event, ...)
+  print('|cFFFF8888'..self:GetName()..':OnEvent()|r', event, ...)
+  if hotkeyEvents[event] then
+    for i, func  in ipairs(hotkeyEvents[event]) do
+
+      if self[func] then
+        print('->|cFF88FF00', func)
+        self[func](self, event, ...)
+      end
+    end
+  end
+  return true
+end
+
+function hotkey:Setup ()
+  print('variables')
+  bindings = kb.GetBindings()
+  self:player()
+  for event, triggers in pairs(hotkeyEvents) do
+    print('setup', event, triggers)
+    self:RegisterEvent(event)
+  end
+
+end
+
+function hotkey:OnLoad()
+  hooksecurefunc("ActionBarButtonEventsFrame_RegisterFrame", hotkey.RegisterFrame)
+
+end
+
+function hotkey:Update()
+  print('--|cFF00FF00ui|r')
+  self:player()
+  hotkey:pet()
+  hotkey:binding()
+end
+
+
+function hotkey:world()
+  print('--|cFF00FF00world|r')
+  -- needs to be delayed so it isn't fired 50 times at login
+  if not hotkeyEvents["UPDATE_BINDINGS"] then
+    hotkeyEvents["UPDATE_BINDINGS"] = {"binding"}
+    self.UPDATE_BINDINGS = self.UpdateFromEvent
+    self:RegisterEvent("UPDATE_BINDINGS")
+  end
+
+  self:player()
+  self:pet()
+end
+
+-- requires all these arguments since non-actionbar buttons don't have all of said methods
+local kprint = (DEVIAN_WORKSPACE and function(...) _G.print('HotKeyUpdate', ...) end) or function() end
+function hotkey:UpdateHotKey(frame, actionType, actionID, hasAction)
+  bindings = kb.GetBindings()
+
+  if hasAction then
+    local indexKey = kb.FormatActionID(actionType, actionID)
+
+
+
+    local actionName
+    if actionType == 'spell' then
+      actionName = GetSpellInfo(actionID)
+    elseif actionType == 'macro' then
+      actionName = GetMacroInfo(actionID)
+    elseif actionType == 'summonmount' then
+      actionName = C_MountJournal.GetMountInfoByID(actionID)
+    elseif actionType == 'item' then
+      actionName = GetItemInfo(actionID)
+    end
+    kprint('  actionKey:', indexKey)
+
+    local binds = bindings[indexKey]
+    if binds and (not frame.HotKey:IsVisible()) then
+      kprint(frame:GetName(), '|cFF88FF00'..indexKey..'|r',  hasAction, actionName)
+      local bindingsText = kb.BindingString(unpack(binds))
+
+      if not hotkeyText[frame] then
+        kprint('-new hotkey element')
+        hotkeyText[frame] = frame:CreateFontString(frame:GetName()..'SkeletonKey', 'OVERLAY')
+        hotkeyText[frame]:SetFont(frame.HotKey:GetFont())
+        hotkeyText[frame]:SetTextColor(frame.HotKey:GetTextColor())
+        hotkeyText[frame]:SetPoint('TOPRIGHT', frame.HotKey, 'TOPRIGHT')
+
+        hooksecurefunc(frame.HotKey, 'SetVertexColor', function(self, r,g,b,a)
+          hotkeyText[frame]:SetTextColor(r,g,b,a)
+        end)
+      end
+
+      if actionType == 'macro' then
+        local name, rank, spellID = GetMacroSpell(actionID)
+        if spellID and bindings['spell_'..spellID] then
+          bindingsText = kb.BindingString(unpack(bindings['spell_'..spellID]))
+        end
+
+      end
+
+
+      hotkeyText[frame]:SetText(bindingsText)
+      hotkeyText[frame]:Show()
+      kprint('   |cFF00FFFF', frame:GetName(), '|cFFFFFF00'..tostring(bindingsText)..'|r')
+
+      return
+    end
+  end
+
+  if hotkeyText[frame] then
+    local oldText = hotkeyText[frame]:GetText()
+    if oldText then
+      hotkeyText[frame]:SetText(nil)
+      print('|cFFFF4400' .. frame:GetName() .. '|r', 'removed text', oldText)
+    end
+  end
+end
+
+local foundSlots = {}
+function hotkey:actionbar()
+  print('--|cFF00FF00actionbar|r')
+  wipe(actionFrames)
+  wipe(foundSlots)
+
+
+  bindings = kb.GetBindings()
+  for k,v in pairs(bindings) do
+    print(k, #v)
+  end
+
+
+  if ActionBarButtonEventsFrame.frames then
+    for index, frame in ipairs(ActionBarButtonEventsFrame.frames) do
+      local slot = frame.action
+      local actionType, actionID = GetActionInfo(slot)
+      local hasAction = HasAction(slot)
+
+      actionSlots[slot] = frame
+      if hasAction then
+        local indexKey = kb.FormatActionID(actionType, actionID)
+
+        actionFrames[indexKey] = actionFrames[indexKey] or {}
+        if not tContains(actionFrames[indexKey], frame) then
+          tinsert(actionFrames[indexKey], frame)
+          actionIndex[slot] = indexKey
+        end
+        local actionName = ''
+        if actionType == 'macro' then
+          actionName = GetMacroInfo(actionID)
+          elseif actionType == 'spell' then
+          actionName = GetSpellInfo(actionID)
+        end
+
+        if bindings[indexKey] then
+          print(indexKey, actionName)
+        for k,v in pairs(bindings[indexKey]) do
+          print(k,v)
+        end
+        end
+
+
+       --tinsert(foundSlots, '|cFFFFFF00#'..index .. '|r ' .. tostring(indexKey).. '('..(bindings[indexKey] and table.concat(bindings[indexKey],' ') or '-')..')')
+      end
+
+
+      self:UpdateHotKey(frame, actionType, actionID, hasAction)
+    end
+  end
+
+  --print('found slots: ', table.concat(foundSlots, ', '))
+end
+
+function hotkey:actionslot (event, slot)
+  local actionType, actionID = GetActionInfo(slot)
+  print(actionSlots[slot], event, actionType, actionID, HasAction(slot))
+  local frame = actionSlots[slot]
+  if frame then
+    self:UpdateHotKey(frame, actionType, actionID, HasAction(slot))
+  end
+
+end
+
+function hotkey:player()
+  print('player')
+  self:actionbar()
+end
+
+
+
+function hotkey:pet(event, arg1)
+  print('--|cFF00FF00pet|r')
+  if event == 'UNIT_PET' and arg1 == 'player' then
+    if PetHasActionBar() and UnitIsVisible("pet") then
+      self:wrapEvent('PET_UI_CLOSE', 'pet')
+      self:wrapEvent('PET_BAR_UPDATE', 'pet')
+    else
+      self:unwrapEvent('PET_UI_CLOSE')
+      self:unwrapEvent('PET_BAR_UPDATE')
+      return
+    end
+  end
+
+  for i=1, NUM_PET_ACTION_SLOTS, 1 do
+    local button = _G['PetActionButton'.. i]
+    --print(button:GetName())
+    for k, v in pairs(button) do
+      --print(' ', k, type(v))
+    end
+  end
+end
+
+
+--- used to pick up changes from user interaction
+local changeIndex = 1
+function hotkey:binding ()
+  local changeNum = #kb.ChangedBindings
+  cprint('--|cFF00FF00binding|r')
+  if changeNum >= changeIndex then
+
+
+    for i = changeIndex, changeNum do
+      cprint(changeIndex,'of', changeNum)
+      local actionType, actionID, name = unpack(kb.ChangedBindings[i])
+      local actionKey = kb.FormatActionID(actionType, actionID)
+      local frames = actionFrames[actionKey]
+      if frames then
+        for i, frame in ipairs(frames) do
+          cprint('|cFFFF0088'..actionKey..'|r', frame)
+          if frame then
+            self:UpdateHotKey(frame , actionType, actionID, HasAction(frame.action))
+          end
+        end
+      else
+        cprint('no frames picked up, rebuild')
+        self:actionbar()
+      end
+
+      changeIndex = i + 1
+    end
+
+  end
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Import.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,209 @@
+-- KrakTool
+-- Import.lua
+-- Created: 7/10/2016 6:20 AM
+-- %file-revision%
+-- Tools for first-time setup and migration from other addons.
+
+local kb = LibStub("LibKraken").register(KeyBinder, 'Import')
+local print = DEVIAN_WORKSPACE and  function(...) print('kbi', ...) end or function() end
+
+
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME = "Summon Random Favorite Mount"
+local results = {}
+local importSet = setmetatable({}, {__tostring = function() return 'Global Bindings' end})
+kb.importTypes = {}
+
+local lineNumber = 0
+local usedBinds, usedKeys = {}, {}
+local profileName = ''
+local specProfile
+local msg = function(...) return KeyBinderImportLog:AddMessage(...) end
+
+--- Core interfaces
+kb.ImportScan = function()
+  -- hang onto this
+  SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
+
+  local hasAnyData = false
+  for addon, module in pairs(kb.importTypes) do
+    if IsAddOnLoaded(addon) then
+      local hasData = module.GetChanges(importSet)
+      if hasData then
+        kb:print("|cFFFFFF00"..addon.." is currently enabled.")
+      end
+
+      hasAnyData = (hasData or hasAnyData)
+    end
+  end
+  if hasAnyData then
+    kb:print("You can use /skb import |cFF00FF00<AddOn Name>|r to attempt to copy settings related to this character. This will also disable that AddOn.")
+  end
+
+
+  kb.ui()
+  --KeyBinderImportLog:Show()
+
+  return importSet
+end
+
+kb.ImportCommmit = function(self, text)
+  kb.db.buttons = importSet.buttons
+  kb.db.bound = importSet.bound
+  kb.db.commands = importSet.commands
+  kb.db.bindings = importSet.bindings
+  kb.db.macros = importSet.macros
+  kb.db[profileName] = importSet[profileName]
+  kb.profile(profileName)
+
+
+  kb:ApplyAllBindings()
+  kb.ui()
+  kb:print('Imported settings applied! Note that you will need to change active specializations and run the command again to create their respective Char+Spec profiles.')
+end
+
+--- Key Scan
+kb.importTypes.KeyBinder = {}
+kb.importTypes.BindPad = {}
+local kbi = kb.importTypes.KeyBinder
+local bp = kb.importTypes.BindPad
+
+--- BindPad import process
+-- because bindpad doesn't store class info, it is not possible to discern the identity of primary/secondary
+
+local GetBindPadSlot = function(profile, slot, data)
+  local actionType = data.type:lower()
+  local command = data.action:gsub('CLICK BindPadKey:%s*', '')
+  local clickAction = command:match('BindPadMacro:%s*(.+)%s*$')
+  local macroName, macroText
+  if clickAction then
+    local clickType = 'spell'
+    if clickAction == SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME then
+      clickType = 'mount'
+      clickAction = 0
+    else
+      clickType = 'spell'
+    end
+
+    macroName, macroText, command = kb.RegisterAction(nil, clickType, clickAction)
+    profile.macros[macroName] = {macroText, command}
+  end
+  print('[|cFF00FF00'.. data.type .. '|r] |cFFFF00FF' .. data.action .. '|r :: "' .. tostring(clickAction) .. '"')
+
+  results[actionType] = (results[actionType] or 0) + 1
+  profile.buttons[slot] = {
+    command,
+    command:gsub(data.type, ''),
+    data.texture
+  }
+  profile.commands[command] = slot
+
+  if usedBinds[data.action] then
+    local key1, key2 = unpack(usedBinds[data.action])
+    print('adding keybind |cFF00FFFF' .. command .. ' |cFF00FF00' .. (key1) .. ' |cFF00FF00' .. (key2 or ''))
+    profile.bindings[key1] = command
+
+    if key2 then
+      profile.bindings[key2] = command
+    end
+
+    usedBinds[data.action] = nil
+
+    msg('|cFF00FFFF'..tostring(profile)..'|r '..slot..': ' .. '|cFF00FFFF' .. command .. '|r |cFF00FF00' .. (key1 or '') .. (key2 and ('|r, |cFF00FF00key2') or '') )
+  else
+    print('|cFFFF4400no binding|r ' .. command)
+  end
+end
+
+local GetBindPadProfile = function(profile, bp)
+  local bpnames = {'CharacterSpecificTab1', 'CharacterSpecificTab2', 'CharacterSpecificTab3'} -- won't need this ever again, let it fall into gc
+
+  -- resolve spec ID
+  local specID = GetSpecialization()
+  local globalID = GetSpecializationInfo(GetSpecialization())
+  local activeProfile
+
+  -- setup string coercions for debugging
+  setmetatable(profile,{__tostring = function(t) return 'Character' end})
+  if GetNumSpecGroups() > 1 then
+    local activeGroup = GetActiveSpecGroup()
+    activeProfile = bp.profileForTalentGroup[activeGroup]
+    profile[specID] = kb.InitProfile(profile[specID] or {})
+    specProfile = profile[specID]
+    setmetatable(specProfile,{__tostring = function(t) return 'Spec #'..specID end})
+  end
+
+  print('-')
+
+  results.spell = 0
+  results.macro = 0
+  results.click = 0
+  results.item = 0
+
+  table.wipe(usedBinds) -- [action] = {key1, key2}
+  table.wipe(usedKeys)  -- [key]    = "action"
+  for id, bpProfile in ipairs(bp) do
+
+    local slots = 0
+
+    if bpProfile.AllKeyBindings then
+      for binding, command in pairs(bpProfile.AllKeyBindings) do
+        -- each binding value
+        if not usedKeys[binding] then
+          usedKeys[binding] = command
+        end
+
+        usedBinds[command] = usedBinds[command]  or {}
+        tinsert(usedBinds[command], binding)
+      end
+    end
+
+
+
+    for i, name in ipairs(bpnames) do
+      -- each tab
+      if bpProfile[name] then
+        for slot, data in pairs(bpProfile[name]) do
+          if type(data) == 'table' then
+            slots = slots + 1
+
+
+            local profile = (id == activeProfile) and specProfile or profile
+            lineNumber = lineNumber + 1
+            GetBindPadSlot(profile, slot, data)
+
+          else
+            --print(type(data), slot, data)
+          end
+        end
+      end
+    end
+  end
+end
+
+
+bp.GetChanges = function(importSet)
+  -- ensure that subtables are there
+  kb.InitProfile(importSet)
+  if BindPadVars then
+    local globalSlots = 0
+    for k,v in pairs(BindPadVars) do
+      --print(k, type(k))
+      if type(k) == 'string' then
+        local realm, name = k:match("^PROFILE_([%S]+)_(%S+)$")
+        if realm == kb.playerRealm and name == kb.playerName then
+          profileName = realm .. '_' .. name
+          msg('Found character profile: |cFFFFFF00'.. tostring(realm) .. '|r-|cFF00FFFF'..tostring(name)..'|r')
+          importSet[profileName] = kb.InitProfile({})
+          importSet[profileName].imported = true
+          GetBindPadProfile(importSet[profileName], v)
+        end
+      elseif type(k) == 'number' and type(v) == 'table' then
+        globalSlots = globalSlots + 1
+        GetBindPadSlot(importSet, globalSlots, v)
+      end
+    end
+  end
+
+  return results
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/KeyButton.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,710 @@
+-- SkeletonKey
+-- KeyButton.lua
+-- Created: 7/28/2016 11:26 PM
+-- %file-revision%
+-- Deals with display and manipulation of binding slots
+
+local _, kb = ...
+local print = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('SkeletonKey', ...) end or function() end
+local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end
+local L = kb.L
+local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs
+local _G, unpack, select, tostring = _G, unpack, select, tostring
+local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo
+local GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon = GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon
+local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
+local GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown, IsShiftKeyDown = GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown,IsShiftKeyDown
+local GetBindingKey, GetProfessionInfo = GetBindingKey, GetProfessionInfo
+local GetMountInfoByID, GetPetInfoByPetID = C_MountJournal.GetMountInfoByID, C_PetJournal.GetPetInfoByPetID
+local skb = SkeletonKeyButtonMixin
+local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION, CURSOR_TEXTURE
+local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
+local BORDER_UNASSIGNED = {0.6,0.6,0.6,1}
+local BORDER_ASSIGNED = {1,1,1,1}
+local BORDER_DYNAMIC = {1,1,0,1}
+local BORDER_PENDING = {1,0.5,0,1 }
+local BUTTON_HEADERS = {
+  ['spell'] = SPELLS,
+  ['macro'] = MACRO,
+  ['petaction'] = PET,
+  ['mount'] = MOUNT,
+  ['battlepet'] = BATTLEPET,
+
+
+  [5] = PROFESSIONS_FIRST_AID,
+  [7] = PROFESSIONS_COOKING,
+  [9] = PROFESSIONS_FISHING,
+  [10] = PROFESSIONS_ARCHAEOLOGY,
+
+}
+
+local PROFESSION_HEADERS = {
+  [1] = 'Profession 1',
+  [2] = 'Profession 2',
+  [3] = 10,
+  [4] = 7,
+  [5] = 9,
+  [6] = 5
+}
+
+
+-- This is needed to identify a spells that aren't reflected by GetCursorInfo()
+kb.OnPickupPetAction = function(slot, ...)
+  local isPickup = GetCursorInfo()
+  print(slot, ...)
+  if kb.PetCache.action[slot] then
+    if isPickup then
+      local key, _, texture = unpack(kb.PetCache.action[slot])
+      local spellName = _G[key] or key
+      if spellName and kb.PetCache.spellslot[spellName] then
+        CURSOR_SPELLSLOT = kb.PetCache.spellslot[spellName][1]
+        CURSOR_BOOKTYPE = BOOKTYPE_PET
+        CURSOR_TEXTURE = _G[texture] or texture
+      end
+    else
+      CURSOR_SPELLSLOT = nil
+      CURSOR_BOOKTYPE = nil
+      CURSOR_TEXTURE = nil
+    end
+    print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
+  end
+
+  local name, subtext, texture, isToken = GetPetActionInfo(slot)
+  if name then
+    kb.PetCache.action[slot] = {name, subtext, texture, isToken}
+  end
+
+
+  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
+
+end
+
+kb.OnPickupSpellBookItem = function(slot, bookType)
+  print('|cFFFF4400PickupSpellBookItem('.. tostring(slot).. ', '..tostring(bookType)..')')
+  CURSOR_SPELLSLOT = slot
+  CURSOR_BOOKTYPE = bookType
+  CURSOR_TEXTURE = GetSpellBookItemTexture(slot, bookType)
+  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
+end
+
+kb.CreateHooks = function()
+  hooksecurefunc("PickupSpellBookItem", kb.OnPickupSpellBookItem)
+  hooksecurefunc("PickupPetAction", kb.OnPickupPetAction)
+end
+
+
+
+function skb:OnLoad()
+  self:EnableKeyboard(false)
+  self:EnableMouse(true)
+  self:RegisterForDrag('LeftButton')
+  self:RegisterForClicks('AnyUp')
+end
+
+function skb:OnEnter()
+  if not self.command then
+    return
+  end
+  if self.statusText then
+    SkeletonKey.statustext:SetText(self.statusText .. ': '..self.actionName)
+    SkeletonKey.bindingstext:SetText(self.bindingText)
+  end
+
+
+  if kb.db.hoverInput and kb.saveTarget ~= self then
+    self:GetParent():ActivateSlot(self)
+    SkeletonKey:Update()
+  end
+end
+
+function skb:OnLeave()
+  if kb.db.hoverInput and kb.saveTarget == self then
+    self:GetParent():DeactivateSlot(self)
+    SkeletonKey:Update()
+  end
+end
+
+function skb:OnUpdate()
+end
+function skb:OnClick(click)
+  print(self:GetName(), 'OnMouseDown', click)
+  local cursorType = GetCursorInfo()
+  if click == 'LeftButton' then
+    if cursorType then
+      self:DropToSlot()
+    else
+      if self.command and self.isAvailable then
+        if IsShiftKeyDown() then
+          kb.db.stickyMode = true
+          KeyBinderStickyMode:SetChecked(true)
+        end
+        self:GetParent():ActivateSlot(self)
+      end
+    end
+  elseif click == 'RightButton' then
+    self:ReleaseSlot()
+  else
+    kb.ProcessInput(strupper(click))
+  end
+  SkeletonKey:Update()
+end
+
+function skb:OnDragStart()
+  self:PickupSlot()
+end
+
+function skb:OnReceiveDrag(...)
+  self:DropToSlot()
+end
+
+function skb:DropToSlot ()
+  print(self:GetName(),'|cFF0088FFreceived|r')
+  local actionType, actionID, subType, subData = GetCursorInfo()
+  print('GetCursorInfo', GetCursorInfo())
+  if actionType then
+
+    if actionType == 'flyout' then
+      ClearCursor()
+      ResetCursor()
+      return
+    end
+
+
+    local name, icon, _
+    local pickupID, pickupBook
+
+    if actionType == 'spell' then
+      actionID = subData
+      name, _, icon = GetSpellInfo(actionID)
+
+    elseif actionType == 'macro' then
+      name, icon = GetMacroInfo(actionID)
+    elseif actionType == 'petaction' then
+      if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then
+
+        local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
+        local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
+        if spellType == 'PETACTION' then
+          name = spellName
+          actionID = spellText
+          icon = CURSOR_TEXTURE
+        else
+          name, _, icon = GetSpellInfo(spellID)
+          actionID = spellID
+        end
+
+        pickupID = CURSOR_SPELLSLOT
+        pickupBook = CURSOR_BOOKTYPE
+      else
+
+
+      end
+
+    elseif actionType == 'mount' then
+      if subType == 0 then
+        name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
+        actionID = 0
+      else
+        name, _, icon = GetMountInfoByID(actionID)
+      end
+    elseif actionType == 'item' then
+      name = GetItemInfo(actionID)
+      icon = GetItemIcon(actionID)
+    elseif actionType == 'battlepet' then
+
+      local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = GetPetInfoByPetID(actionID)
+      name = customName or petName
+      icon = petIcon
+
+    end
+    local macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)
+    local slotInfo = {
+      command = command,
+      actionName = name,
+      iconPath = icon,
+      actionType = actionType,
+      actionID = actionID,
+      macroName = macroName,
+      macroText = macroText,
+      spellbookSlot = pickupID,
+      spellbookType = pickupBook,
+      assignedKeys = {GetBindingKey(command)}
+    }
+
+    local isAssigned, isBound, assignedBy, boundBy = kb.IsCommandBound(self, command)
+    if isAssigned then
+      local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
+      popup.slot = self
+      popup.text = "Currently assigned in |cFFFFFF00"..tostring(kb.configHeaders[assignedBy]).."|r. Are you sure?"
+      popup.oldProfile = assignedBy
+      popup.args = {slotInfo}
+      SkeletonKey:SetScript('OnMouseWheel', nil) -- disable scrolling
+      StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
+    else
+      kb.currentProfile.buttons[self:GetID()] = slotInfo
+      self:SetSlot(slotInfo)
+      self:UpdateSlot()
+      self.active = nil
+      ClearCursor()
+      ResetCursor()
+    end
+  end
+end
+
+
+do
+  local PickupAction = {
+    spell = _G.PickupSpell,
+    petaction =
+      function(...)
+        -- needs to be enclosed to acquire hooksecurefunc effects
+        _G.PickupSpellBookItem(...)
+      end,
+    macro = _G.PickupMacro,
+    item = _G.PickupItem,
+    mount = _G.C_MountJournal.Pickup
+  }
+  local GetPickupValue = {
+    spell = function(self) return select(7, GetSpellInfo(self.actionID)) end,
+    petaction = function(self) return self.pickupSlot, self.pickupBook end,
+  }
+  function skb:PickupSlot ()
+    if not (self.command and self.isAvailable) then
+      return
+    end
+    print(self.actionType)
+    if self.actionType == 'spell' then
+      -- It can't be picked up if SpellInfo(name) returns void
+      local dummy = GetSpellInfo(self.actionName)
+      if not dummy then
+        return
+      end
+    end
+    if PickupAction[self.actionType] then
+      if GetPickupValue[self.actionType] then
+        PickupAction[self.actionType](GetPickupValue[self.actionType](self))
+      else
+        PickupAction[self.actionType](self.actionID)
+      end
+      self:ReleaseSlot()
+      self:UpdateSlot()
+    end
+  end
+end
+
+
+
+
+--- Updates profile assignment and button contents
+function skb:UpdateSlot (force)
+  local slot = self:GetID()
+
+  if force then
+    if kb.currentProfile.buttons[slot] then
+      print('loading in', slot, kb.db.bindMode)
+      self:SetSlot(kb.currentProfile.buttons[slot])
+    else
+      self:ReleaseSlot()
+    end
+  end
+
+  local borderType = BORDER_UNASSIGNED
+
+  if self.command then
+
+    if not self.isAvailable then
+      borderType = BORDER_DYNAMIC
+      self.ignoreTexture:Show()
+    else
+      self.ignoreTexture:Hide()
+
+      if self.pending then
+        borderType = BORDER_PENDING
+      elseif self.dynamicType then
+        borderType = BORDER_DYNAMIC
+      else
+        borderType = BORDER_ASSIGNED
+      end
+    end
+
+
+    if self.actionType == 'macro' then
+      self.macro:Show()
+    else
+      self.macro:Hide()
+      if self.actionType == 'spell' then
+        local dummy = GetSpellInfo(self.actionName)
+        if not dummy then
+          self.icon:SetDesaturated(true)
+        else
+          self.icon:SetDesaturated(false)
+        end
+
+      end
+    end
+
+
+    if self.dynamicType == 'profession'  then
+      if self.isAvailable then
+
+        self.statusText = '|cFFFFFF00Profession|r'
+        self.bindingText = kb.BindingString(GetBindingKey(self.command))
+      else
+
+        self.statusText = '|cFFFF4400'..PROFESSION_HEADERS[self.dynamicIndex]..'|r'
+        self.actionName = '(#'..self.dynamicIndex..')'
+        self.bindingText ='?'
+      end
+    elseif self.dynamicType == 'talent' then
+
+      self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
+      if self.isAvailable then
+        self.bindingText = kb.BindingString(GetBindingKey(self.command))
+      else
+        if kb.TalentBindings[self.actionID] then
+          print(self.actionID, #kb.TalentBindings[self.actionID])
+          self.bindingText= kb.BindingString(unpack(kb.TalentBindings[self.actionID]))
+        end
+
+      end
+    elseif self.dynamicType == 'petaction' then
+      self.bindingText = kb.BindingString(GetBindingKey(self.command))
+    else
+      self.bindingText = kb.BindingString(GetBindingKey(self.command))
+    end
+
+    local locked, layer = kb.IsCommandBound(self)
+    if locked then
+      self.icon:SetAlpha(0.5)
+    else
+      self.icon:SetAlpha(1)
+    end
+
+
+    if self.actionType == 'spell' then
+      self.icon:SetTexture(GetSpellTexture(self.actionID))
+    end
+    print('|cFF00BBFFUpdateSlot|r:', '['..slot..'] =', self.command, self.bindingText, self.dynamicType, self.isAvailable, self.actionID)
+  else
+    if kb.saveTarget == self then
+      kb.DeactivateSlot(self)
+    end
+
+  end
+
+  self.ignoreTexture:SetShown(self.command and not self.isAvailable)
+
+  if not self.isAvailable then
+    self.bind:SetTextColor(0.7,0.7,0.7,1)
+  else
+    self.bind:SetTextColor(1,1,1,1)
+  end
+
+
+  if kb.saveTarget and kb.saveTarget ~= self then
+    self:SetAlpha(0.25)
+  else
+
+    self:SetAlpha(1)
+  end
+
+  --self.alert:SetShown(self.command and not self.isBound)
+
+  self.icon:SetTexture(self.iconPath)
+
+  self.border:SetColorTexture(unpack(borderType))
+  self.header:SetText(self.statusText)
+  self.bind:SetText(self.bindingText)
+  self.details:SetText(self.actionName)
+end
+
+--- Resets button command
+function skb:ReleaseSlot ()
+  local slot = self:GetID()
+
+
+  if kb.currentProfile.buttons[slot] then
+    kb.currentProfile.buttons[slot] = nil
+  end
+  if self.command then
+    kb.currentProfile.commands[self.command] = nil
+  end
+  local talentName = self.actionName
+  if self.actionType == 'macro' then
+    talentName = GetMacroSpell(self.actionID)
+  end
+    -- remove any matching talent data
+  if talentName and kb.currentProfile.talents[talentName] then
+    kb.currentProfile.talents[talentName] = nil
+  end
+  local droppedKeys = {}
+
+  -- doing removal in second loop to avoid possible iterator shenanigans
+  for k,v in pairs(kb.currentProfile.bindings) do
+    if v == self.command then
+      tinsert(droppedKeys, k)
+    end
+  end
+  if #droppedKeys >=1 then
+    for i, k in ipairs(droppedKeys) do
+      kb.currentProfile.bindings[k] = nil
+    end
+  end
+
+  self.isAvailable = nil
+  self.dynamicType = nil
+  self.bindingText = nil
+  self.statusText = nil
+  self.command = nil
+  self.iconPath = nil
+  self.actionType = nil
+  self.actionID = nil
+  self.actionName = nil
+  self.pickupSlot = nil
+  self.pickupBook = nil
+  self.macroName = nil
+  self.profile = nil
+  self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
+  self:EnableKeyboard(false)
+  self:SetScript('OnKeyDown', nil)
+  self.bindingText = nil
+  self.icon:SetTexture(nil)
+  self.ignoreTexture:Hide()
+
+end
+
+local spells = {}
+local SkeletonKey_GetGenericSpell = function(spellName, spellID, icon)
+  if not spells[spellID] then
+    spells[spellID] = {}
+    spells[spellID].actionType = 'spell'
+    spells[spellID].actionID = spellID
+    spells[spellID].actionName = spellName
+    spells[spellID].iconPath = icon
+    spells[spellID].statusText = '|cFFBBBBBBSpell|r'
+    spells[spellID].dynamicType = nil
+  end
+  return spells[spellID]
+end
+
+local tempInfo = {}
+-- tries to resolve spells from talent overrides/profession book/etc
+local dynamicTypes = {['profession'] = 'ProfessionCache', ['talent'] = 'TalentCache', ['petaction'] = 'PetInfoCache'}
+local SkeletonKey_GetSpellDetails = function(self)
+
+  local spellName, spellID, command, icon = self.actionName, self.actionID, self.command, self.iconPath
+
+
+  cprint(' In:', spellName, spellID, command)
+  cprint(GetSpellInfo(spellName or spellID))
+  local internalName, _, internalIcon, _, _, _, _ = GetSpellInfo(spellName or spellID)
+  local isAvailable = internalName and true
+
+  if internalName and (internalName ~= spellName) then
+    -- it's a binding for the originating spell, leave it as is
+    cprint('  |cFFFF4400spell is an override(', internalName, '~=', spellName,') leave the name info alone')
+    self.statusText = '|cFFFFFF00Spell|r'
+    self.isAvailable = true
+    return
+  end
+
+  -- let's us match spells replaced by talents
+  local info = kb.DynamicSpells[internalName or spellName]
+  if not info then
+    local dynamicType, dynamicIndex, dynamicSubIndex = command:match("(%a+)_(%S+)_(%S+)")
+    if kb.DynamicSpells[dynamicType] then
+      cprint('|cFFFF4400resolving dynamic type index:', internalName, spellName, command)
+      dynamicIndex = tonumber(dynamicIndex)
+      dynamicSubIndex = tonumber(dynamicSubIndex)
+      local cache = kb.DynamicSpells[dynamicType]
+      cprint('type:', dynamicType)
+      if dynamicIndex and cache[dynamicIndex] then
+        info = kb.DynamicSpells[dynamicType][dynamicIndex]
+        cprint('index:', dynamicIndex)
+        if dynamicSubIndex and info[dynamicSubIndex] then
+          info = info[dynamicSubIndex]
+          cprint('sub-index:', dynamicSubIndex)
+        end
+        isAvailable = true
+      end
+    end
+    if not info then
+      info = SkeletonKey_GetGenericSpell(spellName, spellID, internalIcon or icon)
+    end
+  end
+  info.isAvailable = isAvailable
+
+  cprint('|cFF00FF88SpellDetails:|r', info.actionName, info.actionID, info.dynamicType)
+  for k,v in pairs(info) do
+    cprint(' ',k,v)
+    self[k] = v
+  end
+
+  return info
+end
+
+--- Assigns the slot via table copy; any manipulations from this point are temporary and
+function skb:SetSlot(slotInfo)
+  print('slot info', self:GetID())
+
+  for k,v in pairs(slotInfo) do
+    print('  -', k, v)
+    self[k] = v
+  end
+  self.dynamicType = slotInfo.dynamicType
+  local command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook
+  = self.command, self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType
+
+
+  local slot = self:GetID()
+  local isBound = false
+  print('|cFFFFFF00SetSlot|r:', self:GetID())
+  if self.command then
+
+    isBound = kb.IsCommandBound(self, self.command)
+    if actionType == 'spell' then
+      local info = SkeletonKey_GetSpellDetails(self)
+      name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook = self.actionName, self.iconPath, self.actionType, self.actionID, self.macroName, self.macroText, self.spellbookSlot, self.spellbookType
+
+    elseif actionType == 'petaction' then
+      self.dynamicType = 'petaction'
+      local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)')
+
+      if kb.PetCache.subtext[specialType] then
+
+        local info = kb.PetCache.subtext[specialType][tonumber(specialNum)]
+        if info then
+          print('***dynamic pet skill', specialType, specialNum)
+          --[[ i, spellName, subText, spellID,  texture, specialNum[subText ]]
+
+          for k,v in pairs(info) do
+            self[k] = v
+          end
+        end
+
+      end
+      self.statusText = 'Pet Action'
+      self.isAvailable = (kb.PetCache.spellslot[name])
+    elseif actionType == 'macro' then
+      if actionID then
+        -- look for corruption
+        local nameByID, _, bodyByID = GetMacroInfo(actionID)
+        local nameByName, _, bodyByName = GetMacroInfo(name)
+        if (nameByID ~= name) or (bodyByID ~= macroText) then
+          local prevIndex = actionID
+          actionID = GetMacroIndexByName(name)
+          local firstName, _, firstBody = GetMacroInfo(actionID)
+          if (firstName ~= name) or (firstBody ~= macroText) then
+            -- go even deeper
+            for i = 1, GetNumMacros() do
+              local searchName, _ , searchBody = GetMacroInfo(i)
+              if (searchName == name) and (searchBody == macroText) then
+                -- complete match
+                actionID = i
+                break
+              elseif (searchName == name) or (searchBody == macroText) then
+                -- partial match, continue the search
+                actionID = i
+              end
+            end
+          end
+          kb:print('Macro index changed: |cFFFFFF00', actionType, '|r', name, '(was '..tostring(prevIndex)..', now '..tostring(actionID)..')')
+        end
+      else
+        actionID = GetMacroIndexByName(name)
+      end
+      self.statusText = 'Macro'
+      self.isAvailable = true
+    else
+      if not actionID then
+        actionID = command:match("^KeyBinderMacro:(.+)")
+      end
+      self.isAvailable = true
+    end
+
+    if self.isAvailable then
+      local oldCommand = command
+      command = kb.LoadBinding(self)
+      if oldCommand ~= command then
+        print('|cFFFF4400fixing command string', actionType, actionID, name)
+        kb.currentProfile.bound[oldCommand] = nil
+        kb.currentProfile.bound[command] = slot
+        for k,v in pairs(kb.currentProfile.bindings) do
+          if v == oldCommand then
+            kb.currentProfile.bindings[k] = command
+          end
+        end
+      end
+    end
+
+
+    actionID = actionID or 0
+    self:EnableKeyboard(true)
+
+    -- this is done to keep legacy key-values from breaking algorithm assumptions
+    local slotInfo = {
+      command = command,
+      actionName = name,
+      iconPath = icon,
+      actionID = actionID,
+      actionType = actionType,
+      macroName = macroName,
+      macroText = macroText,
+      spellbookSlot = pickupSlot,
+      spellbookType = pickupBook,
+      assignedKeys = {GetBindingKey(command)}
+    }
+    kb.currentProfile.buttons[slot] = slotInfo
+
+    -- Clean up conflicting entries for loaded button
+    local previous = kb.currentProfile.commands[command]
+    if previous ~= slot and kb.buttons[previous] then
+      kb.ReleaseSlot(kb.buttons[previous])
+    end
+
+    local binds = {GetBindingKey(command) }
+    if self.isAvailable and (#binds >= 1) then
+      local found
+      for i, key in ipairs(binds) do
+        if not tContains(self.assignedKeys, key) then
+          tinsert(self.assignedKeys, key)
+          kb.currentProfile.bindings[key] = command
+          kb.currentProfile.bound[command] = true
+          found = true
+        end
+      end
+      if found then
+        kb:print('Recovered key binding for', name)
+      end
+    end
+
+    kb.currentProfile.commands[command] = slot
+  end
+
+
+
+  self.isBound = isBound
+  self.pickupSlot = pickupSlot
+  self.pickupBook = pickupBook
+  self.macroText = macroText
+  self.macroName = macroName
+  self.actionType = actionType
+  self.actionID = actionID
+  self.actionName = name
+  self.command = command
+  self.iconPath = icon
+  self.profile = kb.db.bindMode
+  self:RegisterForDrag('LeftButton')
+end
+
+kb.GetCommandAction = function(command)
+  for i, data in ipairs(kb.loadedProfiles) do
+    if data.commands[command] then
+      if data.buttons[data.commands[command]] then
+        local _, _, _, actionType, actionID = unpack(data.buttons[data.commands[command]])
+        return actionType, actionID
+      end
+    end
+  end
+end
+
--- a/LibKraken/LibKraken.iml	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module type="WEB_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output />
-    <content url="file://$MODULE_DIR$" />
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>
\ No newline at end of file
--- a/LibKraken/LibKraken.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,433 +0,0 @@
---[[
--- KrakynTools
--- AddOn prototyping library.
---
---- Bundles an object into the handler queue, returning the core object, and handlers for debug output and co-routine.
--- Addon name is determined in order of: (string first arg, second arg :GetName(), invoking filename).
--- Once an addon name/object is registered, subsequent calls will return a debugger that reports the file or plugin name.
--- @usage addon, print, wrap = KT.register(name, table) or KT.register(addon) or KT.register(addon, plugin)
--- @param name - name of addon, as found in global varargs
--- @param table - addon table from global varargs
---
--- @param frame - frame object used by addon
--- @param plugin - string name of plugin or an object table to check for lib handlers
---
--- Handlers:
---    :init()                            run immediately after KT sets itself up
---    :profile("Name-TruncatedRealm")    called the first time SavedVars data becomes available
---    :variables()                       called upon variables being available
---    :event(event, ...)                 replaces the event callback
---    :ui()                              called by /ui when activating
---
--- Embedded:
---    NOTES:
---      * `name' is passed as is into CreateFrame, so using nil produce an anonymous frame
---      * `coord' is a 4 or 8 size table unpacked into Texture:SetTexCoords()
---
---    tab = frame:tab(name, tooltip, texture, coord)
---      produces a serial button that changes display tabs
---
---    button = frame:button(name, text, tooltip, onClick)
---      produces a button with OnClick script
---
---    uibutton = frame:uibutton(name, text, tooltip, onClick, texture, coord)
---      produces a header button with desired onClick
---
---
-]]--
-
-local LIBKT_MAJOR, LIBKT_MINOR = "LibKraken", 2
-local KT = LibStub:NewLibrary(LIBKT_MAJOR, LIBKT_MINOR)
-
---GLOBALS: KTErrorFrame, LibKTError, SlashCmdList, SLASH_RL1, SLASH_UI1
-local CreateFrame, debugstack, tostring, select = CreateFrame, debugstack, tostring, select
-local max, unpack, tinsert, tremove = max, unpack, tinsert, tremove
-local ipairs, pairs, xpcall = ipairs, pairs, xpcall
-local type, assert = type, assert
-local IsLoggedIn = IsLoggedIn
-local db
-
-local print = DEVIAN_WORKSPACE and function(...) _G.print('LKT', ...) end or function() end
-local noFunc = function() end
-
-KT.handler = CreateFrame('Frame', 'LibKTHostFrame', UIParent)
-KT.addons = {}
-KT.initStack = {}
-local libInitialized = false
-local registeredHandles = {}
-local initialized = {}
-local enabled = {}
-local handlers = {}
-
-
-
-local debuggers = {}
-local pending = {}
-
-
-local Embed = function (target, template)
-  for k,v in pairs(template) do
-    if not target[k] then
-      print(tostring(target),'<-', k)
-      target[k] = template[k]
-    end
-  end
-end
-
-local LibKT_Error = function(msg)
-  local dstack = debugstack(2)
-  :gsub("Interface\\AddOns\\",'')
-  :gsub("<(.-)>", function(a) return '|cFF00FFFF<'.. a ..'>|r' end)
-
-
-
-  KTErrorFrame.errmsg:SetText(msg)
-  KTErrorFrame.debugstack:SetText(dstack)
-  KTErrorFrame:SetHeight(KTErrorFrame.debugstack:GetStringHeight() + KTErrorFrame.errmsg:GetStringHeight() + 12)
-  KTErrorFrame:Show()
-end
-
-local LibKT_OnLoad = function(self)
-  --- /rl
-  -- ReloadUI shortcut
-  SLASH_RL1 = "/rl"
-  SlashCmdList.RL = function ()
-    ReloadUI()
-  end
-  libInitialized = true
-end
-
-local LibKT_OnEvent = function(self, event, arg1)
-  print(event, arg1)
-  if (event == 'ADDON_LOADED' and arg1 ~= 'Blizzard_DebugTools') or event == 'PLAYER_LOGIN' then
-    if not libInitialized then
-      LibKT_OnLoad(self)
-    end
-
-
-      -- run any init blocks left in the queue
-    for i, addon in  ipairs(KT.initStack) do
-      if not initialized[addon] then
-        print('|cFF0088FF'..tostring(addon)..'|r:init()')
-        if addon.init then
-          xpcall(addon.init, LibKT_Error)
-        end
-
-        if addon.event then
-          addon:SetScript('OnEvent', addon.event)
-        end
-
-        initialized[addon] = true
-      end
-
-      if #addon.modules >= 1 then
-        for i, module in ipairs(addon.modules) do
-          if not initialized[module] then
-            print(i .. ' |cFF0088FF'..tostring(addon)..'|r.|cFF00FFFF'..tostring(module)..'|r:init()')
-            if module.init then
-              xpcall(module.init, LibKT_Error)
-            end
-
-            if module.event then
-              module:SetScript('OnEvent', module.event)
-            end
-            initialized[module] = true
-          end
-        end
-      end
-
-    end
-
-    -- run any variables blocks if player variables are ready 
-    if IsLoggedIn() then
-
-      for i, addon in  ipairs(KT.initStack) do
-        print('|cFF88FF00'..tostring(addon)..'|r')
-        if initialized[addon] then
-          if not enabled[addon] then
-            print('|cFF88FF00'..tostring(addon)..'|r:variables()')
-            if addon.variables then
-              xpcall(addon.variables, LibKT_Error)
-            end
-            enabled[addon] = true
-          end
-
-          if addon.modules and enabled[addon] then
-            for i, module in ipairs(addon.modules) do
-              print(i .. ' |cFF88FF00'..tostring(module)..'|r')
-              if not enabled[module] then
-                if module.variables then
-                  print(i..' |cFF88FF00'..tostring(addon)..'|r.|cFF00FFFF'.. tostring(module)..'|r:variables()')
-                  xpcall(module.variables, LibKT_Error)
-                end
-                enabled[module] = true
-              end
-            end
-          end
-        end
-      end
-    end
-  end
-end
-
---- GUI bits
-local defaultGUIAddon = {}
-do
-  local GetButtonTemplate = function(name, parent, template, onClick)
-    if _G[name] then
-      return _G[name]
-    end
-
-    local button = CreateFrame('Button', name, parent, template)
-    button:RegisterForClicks('AnyUp')
-    button:SetScript('OnClick', onClick)
-    return button
-  end
-
-  local SetButtonAnchor = function(self, collector, anchor, growth)
-    if self:GetID() == 0 then
-      self:SetID(#collector)
-      print('registered TabButton #', self:GetID())
-    end
-
-    if self:GetID() == 1 then
-      self:SetPoint(unpack(anchor))
-    else
-      growth[2] = collector[self:GetID()-1]
-      self:SetPoint(unpack(growth))
-    end
-  end
-
-  defaultGUIAddon.tab = function(self, name, tooltip, texture, coords)
-    local button = GetButtonTemplate(name, self,  'KTTabButton', self.SelectTab)
-    button.icon:SetTexture(texture)
-    button.tooltip = tooltip
-    button:SetSize(unpack(self.tabSize))
-    if coords then
-      button.icon:SetTexCoord(unpack(coords))
-    end
-    SetButtonAnchor(button, self.tabButtons, self.tabAnchor, self.tabGrowth)
-    return button
-  end
-
-  defaultGUIAddon.button = function(self, name, text, tooltip, onClick)
-    local button = GetButtonTemplate(name, self, 'KTButton', onClick)
-
-    button.tooltip = tooltip
-    button:SetText(text)
-    button:SetWidth(max(button:GetWidth(), button:GetFontString():GetStringWidth() + 12))
-
-    SetButtonAnchor(button, self.controls, self.controlsAnchor, self.controlsGrowth)
-    return button
-  end
-
-  defaultGUIAddon.uibutton = function(self, name, text, tooltip, onClick, texture, coords)
-    local button = GetButtonTemplate(name, self, 'KTUIPanelButton', onClick)
-
-    button.tooltip = tooltip
-    button:SetText(text)
-
-    if self.UIPanelIcon then
-      local w, h, anchor, x, y = unpack(self.UIPanelIcon)
-      button.icon:SetTexture(texture)
-      button.icon:SetSize(w, h)
-      button.icon:ClearAllPoints()
-      button.icon:SetPoint(anchor, button, anchor, x, y)
-    end
-
-    if not self.UIPanelSize then
-      button:SetWidth(button:GetFontString():GetStringWidth() + button.icon:GetWidth()/1.5)
-    else
-      button:SetSize(unpack(self.UIPanelSize))
-    end
-    if coords then
-      button.icon:SetTexCoord(unpack(coords))
-    end
-    SetButtonAnchor(button, self.UIPanels, self.UIPanelAnchor, self.UIPanelGrowth)
-    return button
-  end
-end
-
-
-local defaultAddon = {}
-do
-  defaultAddon.print = function(module, ...)
-    local msg = '|cFF00FFFF'..module:GetName()..'|r:'
-    for i = 1, select('#', ...) do
-      msg = msg .. ' ' .. tostring(select(i, ...))
-    end
-    DEFAULT_CHAT_FRAME:AddMessage(msg)
-  end
-
-
-  local tickerQueue = {}
-  local ticker
-  defaultAddon.tick = function()
-
-    if #tickerQueue == 0 then
-      ticker:Cancel()
-      ticker = nil
-    end
-    local func = tremove(tickerQueue, 1)
-    if func then
-      --print('#', #tickerQueue)
-      func()
-    end
-  end
-
-  defaultAddon.next = function(f)
-    if not ticker then
-      --print('create ticker')
-      ticker = C_Timer.NewTicker(.001, defaultAddon.tick)
-    end
-    tinsert(tickerQueue, f)
-
-    return #tickerQueue
-  end
-
-
---- default OnEvent
-
-  local EmbedEventScript = function (handler)
-    if not handler.SetScript then
-       return
-    end
-    print('|cFF00FF88binding', handler:GetName())
-    -- enclose so .event can be overridden post-register
-    handler:SetScript('OnEvent', function(self, event,...)
-      print('|cFFFF4400' .. tostring(self) .. '|r', event, ...)
-      print(self.event)
-      print(self[event])
-      self.event(self, event, ...)
-    end)
-    handler.unhandled = 0
-    handler.missed = 0
-    handler.handled = 0
-    handler.firstEvent = false
-  end
-
-
-  local isHandled = false
-  local nodebug = false
-  defaultAddon.event = function (self, event, ...)
-    print('what')
-    --- reset state
-    if self[event] then
-      print('|cFFFFFF00'.. tostring(self) .. '|r', event)
-      print(self.debug)
-      self.debug(event, ...)
-      self[event](self, event, ...)
-      self.missed = 0
-      self.handled = self.handled + 1
-      isHandled = true
-    else
-      self.firstEvent = false
-      self.unhandled = self.unhandled + 1
-      self.missed = self.missed + 1
-    end
-    return
-  end
-  defaultAddon.wrap = function(addon, module, name)
-    local moduleName = name or tostring(module)
-    print(addon, module)
-    print('|cFF0088FF'..tostring(addon)..'|r:wrap(|cFF00FFFF'.. moduleName .. '|r|cFFFFFF00)|r')
-
-    addon.modules = addon.modules or {}
-    tinsert(addon.modules, module)
-
-
-    if (module.DEVIAN_PNAME and DEVIAN_PNAME == module.DEVIAN_PNAME) or ((not module.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
-      debuggers[module] = function(...) _G.print(moduleName, ...) end
-    else
-      debuggers[module] = noFunc
-    end
-    module.debug = debuggers[module]
-
-    Embed(module, defaultAddon)
-    EmbedEventScript(module)
-
-    return debuggers[module]
-  end
-
-  defaultAddon.GetName = function(self) return tostring(self) end
-end
-
---- Frame registration
-KT.register = function(addon, arg, noGUI)
-  --print('register(', addon, arg, ')')
-  local name, handler
-  if type(addon) == 'string' and type(arg) == 'table' then
-    name = addon
-    -- it's a string, i.e. file vararg was passed
-    if _G[addon] then
-      -- check if it's the name of a frame and use that
-      handler = _G[addon]
-    else
-      -- re-arrange
-      handler = arg
-    end
-  else
-    handler = addon
-    assert(type(handler) == 'table', 'Usage: KT.register(name, table) or KT.register(table, plugin)')
-  end
-
-
-  local printName
-  local isModule
-
-  local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
-  if registeredHandles[handler] then
-    -- addon is already register; treat second argument as plugin target
-    isModule = true
-    if type(arg) == 'table' then
-      local mt = getmetatable(arg)
-      setmetatable(arg, {__index = mt.__index, __tostring = function() return scriptName end})
-      local debugger = handler:wrap(arg)
-      return handler, debugger
-    else
-      print(' + "|cFF00FFFF'..scriptName..'|r"')
-    end
-  else
-    -- new addon
-    --local scriptName = debugstack(2,1,0):match(".+\\(%S+)%.lua")
-    local mt = getmetatable(handler)
-    local nmt = {__index = mt.__index, __tostring = function() return scriptName end }
-    handler = setmetatable(handler, nmt)
-    Embed(handler, defaultAddon)
-    name = tostring(handler)
-    registeredHandles[handler] = name
-    if handler.SetScript then
-      handler:SetScript('OnEvent', function(...) handler.event(...) end)
-      handler.unhandled = 0
-      handler.missed = 0
-      handler.handled = 0
-      handler.firstEvent = false
-    end
-    handler.modules = {}
-    tinsert(KT.initStack, handler)
-
-    if not noGUI then
-      handler.UIPanelAnchor = {'TOPLEFT', handler, 'TOPLEFT', 12, -12 }
-      handler.UIPanelGrowth = {'TOPLEFT', 'TOPRIGHT', 14, 0}
-      Embed(handler, defaultGUIAddon)
-    end
-
-    print('|cFF0088FF'..tostring(addon)..'|r')
-  end
-
-  local debugFunc = noFunc
-  --@debug@
-  local debugID = isModule and name or handler
-  if (handler.DEVIAN_PNAME and DEVIAN_PNAME == handler.DEVIAN_PNAME) or ((not handler.DEVIAN_PNAME) and DEVIAN_WORKSPACE) then
-    debuggers[debugID] = debuggers[debugID] or function(...) _G.print(name, ...) end
-    debugFunc = debuggers[debugID]
-  end
-
-  handler.debug = debugFunc
-  --@end-debug@
-  return handler, debugFunc
-end
-
-KT.handler:RegisterEvent('ADDON_LOADED')
-KT.handler:RegisterEvent('PLAYER_LOGIN')
-KT.handler:SetScript('OnEvent', LibKT_OnEvent)
\ No newline at end of file
--- a/LibKraken/LibKraken.toc	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 70100
-## Title: !LibKraken
-## Notes: Addon framework for dinosaurs
-## Author: Krakyn
-## Version: 1.0-@project-revision@
-## X-Category: Interface Enhancements
-## DefaultState: Enabled
-## LoadOnDemand: 0
-## OptionalDeps: Ace3, LibStub
-LibStub\LibStub.lua
-LibKraken.xml
--- a/LibKraken/LibKraken.xml	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +0,0 @@
-<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="LibKraken.lua" />
-
-  <Font name="KTLogString" font="Fonts\ARIALN.TTF" height="14" justifyH="LEFT" justifyV="TOP" inherits="NumberFontNormal" virtual="true">
-    <Color a="1" r="1" g="1" b="1" />
-  </Font>
-
-  <Font name="KTHeaderFont" height="18" font="Fonts\skurri.ttf" outline="NORMAL" justifyH="LEFT" justifyV="TOP" virtual="true" />
-  <Font name="KTHeader2Font" height="14" font="Fonts\skurri.ttf" outline="NORMAL" justifyH="LEFT" justifyV="TOP" virtual="true" />
-  <Font name="KTUIPanelFont" justifyH="LEFT" virtual="true"  inherits="NumberFontNormal"  />
-  <Font name="KTMacroButtonFont" height="12" font="Fonts\ARIALN.TTF" justifyH="LEFT" virtual="true" >
-    <Color a="1" r="1" g="1" b="1" />
-  </Font>
-
-
-  <Button name="KTButton" parentArray="controls" virtual="true">
-    <Size x="72" y="28" />
-    <Scripts>
-      <OnEnter>
-        if self.tooltip then
-          GameTooltip:SetOwner(self)
-          GameTooltip:SetAnchorType('ANCHOR_TOPRIGHT')
-          GameTooltip:SetText(self.tooltip)
-          GameTooltip:Show()
-        end
-      </OnEnter>
-      <OnLeave>
-        GameTooltip:Hide()
-      </OnLeave>
-    </Scripts>
-    <NormalFont style="NumberFontNormal" />
-    <NormalTexture setAllPoints="true">
-      <Color a="1" r="0" g=".4" b="1" />
-    </NormalTexture>
-    <DisabledTexture>
-      <Color a="1" r="0.5" b="0.5" g="0.5" />
-    </DisabledTexture>
-    <DisabledColor a="0.5" r="1" g="1" b="1" />
-    <PushedTexture>
-      <Color a="1" r="1" g="0.25" b="0.25" />
-    </PushedTexture>
-    <HighlightTexture alphaMode="ADD">
-      <Color a="0.25" r="1" g="0" b=".5" />
-    </HighlightTexture>
-  </Button>
-
-  <!-- style for Blizzard UIPanel activators
-    // The template anchor gets overwritten for successive iterations -->
-  <Button name="KTUIPanelButton" virtual="true" parentArray="UIPanels">
-    <Size x="84" y="24" />
-    <Scripts>
-      <OnEnter>
-        if self.tooltip then
-          GameTooltip:SetOwner(self)
-          GameTooltip:SetAnchorType('ANCHOR_TOPRIGHT')
-          GameTooltip:SetText(self.tooltip)
-          GameTooltip:Show()
-        end
-      </OnEnter>
-      <OnLeave>
-        GameTooltip:Hide()
-      </OnLeave>
-    </Scripts>
-    <Layers>
-      <Layer level="OVERLAY">
-        <Texture parentKey="icon" />
-      </Layer>
-    </Layers>
-
-    <ButtonText>
-      <Anchors>
-        <Anchor point="LEFT" x="14" y="0" />
-      </Anchors>
-    </ButtonText>
-
-    <NormalFont style="KTUIPanelFont" />
-    <NormalTexture>
-      <Color a="1" r=".24" g=".24" b=".24" />
-    </NormalTexture>
-    <PushedTexture>
-      <Color a="1" r="0" g="0" b="0" />
-    </PushedTexture>
-    <HighlightTexture alphaMode="ADD">
-      <Size x="32" />
-      <Color a="1" r="1" g="1" b="1" />
-      <Gradient orientation="HORIZONTAL">
-        <MaxColor r=".25" g=".25" b=".25"/>
-        <MinColor  r="0" g="0" b="0" />
-      </Gradient>
-    </HighlightTexture>
-
-  </Button>
-
-  <Button name="KTTabButton" virtual="true" parentArray="tabButtons">
-    <Scripts>
-      <OnEnter>
-
-        if not self.tooltip then
-          return
-        end
-        GameTooltip:SetOwner(self)
-        GameTooltip:SetAnchorType('LEFT')
-        GameTooltip:SetText(self.tooltip)
-        GameTooltip:Show()
-      </OnEnter>
-      <OnLeave>
-
-        if not self.tooltip then
-          return
-        end
-        GameTooltip:Hide()
-      </OnLeave>
-    </Scripts>
-    <Size x="40" y="40" />
-    <Layers>
-      <Layer level="BACKGROUND">
-        </Layer>
-      <Layer level="BORDER">
-        <Texture parentKey="icon">
-          <Color a="1" r="1" g="0" b="0" />
-          <Anchors>
-            <Anchor point="TOPLEFT" x="2" y="-2"/>
-            <Anchor point="BOTTOMRIGHT" x="-2" y="2"/>
-          </Anchors>
-        </Texture>
-      </Layer>
-      <Layer level="ARTWORK">
-      </Layer>
-    </Layers>
-
-    <NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
-      <Anchors>
-        <Anchor point="TOPLEFT" x="-12" y="12"/>
-        <Anchor point="BOTTOMRIGHT" x="13" y="-13"/>
-      </Anchors>
-    </NormalTexture>
-    <PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
-    <HighlightTexture alphaMode="ADD" file="Interface\Buttons\ButtonHilight-Square"/>
-  </Button>
-
-  <!-- inherited to generate event feedback -->
-  <Frame name="KTDebugTemplate" virtual="true">
-    <Layers>
-      <Layer level="OVERLAY">
-        <FontString inherits="GameFontNormal" parentKey="status" text="text thing here">
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="KTLogString" parentKey="logfirst" text="First">
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" x="0" y="0" relativeKey="$parent.status" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="KTLogString" parentKey="logdiff" text="Different">
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeKey="$parent.logfirst" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="KTLogString" parentKey="log" text="Last">
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeKey="$parent.logdiff" />
-          </Anchors>
-        </FontString>
-      </Layer>
-    </Layers>
-  </Frame>
-
-
-
-  <Frame name="KTErrorFrame" parent="UIParent" toplevel="true" movable="true" enableMouse="true" hidden="true" clampedToScreen="true">
-    <Size x="450" y="280" />
-    <Anchors>
-      <Anchor point="CENTER" />
-    </Anchors>
-    <Scripts>
-      <OnLoad>
-        self:RegisterForDrag('LeftButton')
-      </OnLoad>
-      <OnDragStart>
-        self:StartMoving()
-      </OnDragStart>
-      <OnDragStop>
-        self:StopMovingOrSizing()
-      </OnDragStop>
-    </Scripts>
-    <Layers>
-      <Layer level="BACKGROUND">
-        <Texture setAllPoints="true">
-          <Color a="1" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-      <Layer level="OVERLAY">
-        <FontString inherits="NumberFont_Outline_Huge" text="KrakTool Error">
-          <Anchors>
-            <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="NumberFont_Outline_Large" parentKey="errmsg" justifyH="LEFT" spacing="3">
-          <Anchors>
-            <Anchor point="TOP" />
-            <Anchor point="LEFT" />
-            <Anchor point="RIGHT" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="NumberFont_Outline_Large" parentKey="debugstack" justifyH="LEFT" spacing="3">
-          <Anchors>
-            <Anchor point="LEFT" />
-            <Anchor point="RIGHT" />
-            <Anchor point="TOP" relativePoint="BOTTOM" relativeKey="$parent.errmsg" x="0" y="-8" />
-          </Anchors>
-        </FontString>
-      </Layer>
-    </Layers>
-  </Frame>
-</Ui>
\ No newline at end of file
--- a/LibKraken/LibStub/LibStub.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,30 +0,0 @@
--- LibStub is a simple versioning stub meant for use in Libraries.  http://www.wowace.com/wiki/LibStub for more info
--- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
-local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2  -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
-local LibStub = _G[LIBSTUB_MAJOR]
-
-if not LibStub or LibStub.minor < LIBSTUB_MINOR then
-	LibStub = LibStub or {libs = {}, minors = {} }
-	_G[LIBSTUB_MAJOR] = LibStub
-	LibStub.minor = LIBSTUB_MINOR
-	
-	function LibStub:NewLibrary(major, minor)
-		assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
-		minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
-		
-		local oldminor = self.minors[major]
-		if oldminor and oldminor >= minor then return nil end
-		self.minors[major], self.libs[major] = minor, self.libs[major] or {}
-		return self.libs[major], oldminor
-	end
-	
-	function LibStub:GetLibrary(major, silent)
-		if not self.libs[major] and not silent then
-			error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
-		end
-		return self.libs[major], self.minors[major]
-	end
-	
-	function LibStub:IterateLibraries() return pairs(self.libs) end
-	setmetatable(LibStub, { __call = LibStub.GetLibrary })
-end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SkeletonKey.iml	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="LUA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/../LibKraken" />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SkeletonKey.lua	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,409 @@
+--------------------------------------------
+-- SkeletonKey
+-- Krakyn-Mal'Ganis
+-- @project-revision@ @project-hash@
+-- @file-revision@ @file-hash@
+-- Created: 6/16/2016 3:47 AM
+--------------------------------------------
+-- Header script
+
+local addonName, kb = ...
+local print = DEVIAN_WORKSPACE and function(...) print('SK',...) end or nop
+SkeletonKeyMixin = {
+  scrollCache = {},
+  tabButtons = {},
+  keyButtons = {},
+  panelButtons = {},
+  numTabs = 0,
+}
+kb.L = setmetatable({}, {
+  __call = function(t, k, ...) return format(t[k] or k, ...) end
+})
+local L = kb.L
+
+--- Caps Lock literals
+L.UNSELECTED_TALENT_ASSIGNED = '|cFF00FF00%s|r added for |cFFFFFF00%s|r (%s).'
+L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
+L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
+L.BINDING_FAILED_PROTECTED = '|cFFFF4400Cannot use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400). Uncheck "Safety" to ignore this restraint.|r'
+
+
+local BINDING_TYPE_SPECIALIZATION = 3
+local BINDING_TYPE_CHARACTER = 2
+local BINDING_TYPE_GLOBAL = 1
+kb.configTitle = {
+  [BINDING_TYPE_GLOBAL] = L('Global Binds'),
+  [BINDING_TYPE_CHARACTER] = L('%%s'),
+  [BINDING_TYPE_SPECIALIZATION] = L('%%s')
+}
+kb.configDescription = {
+  [BINDING_TYPE_GLOBAL] = L('The bindings are applied globally.'),
+  [BINDING_TYPE_CHARACTER] = L('Applied when you log onto this character.'),
+  [BINDING_TYPE_SPECIALIZATION] = L('Applied when you select this specialization.'),
+}
+
+
+kb.ChangedBindings = {}
+kb.SystemBindings = {}
+kb.ActionTypes = {}
+kb.TalentBindings = {}
+kb.PetCache = {
+  spell = {},
+  spellslot = {},
+  action = {},
+  special = {},
+  subtext = {}
+}
+kb.DynamicSpells = {
+  profession = {},
+  petaction = {},
+  talent = {},
+}
+kb.TalentCache = {}
+kb.ProfessionCache = {}
+kb.pendingCalls = {}
+kb.pendingAttributes = {}
+
+kb.configHeaders = {}
+kb.loadedProfiles = {}
+kb.orderedProfiles = {}
+kb.buttons = {}
+kb.macros = {}
+kb.bindings = {}
+kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed
+kb.talentFrames = {}
+kb.professionFrames = {}
+
+-- these are sent to plugin
+
+
+local db
+local _G = _G
+local UnitName, SelectedRealmName, InCombatLockdown, UnitClass = UnitName, SelectedRealmName, InCombatLockdown, UnitClass
+local tostring, select, tinsert, pairs = tostring, select, tinsert, pairs
+local concat, wipe = table.concat, table.wipe
+local classHeader, className, classID = '', '', 0
+
+
+local CloseButton_OnClick = function()
+
+  kb.db.showUI = false
+  print(kb.db.showUI)
+  SkeletonKey:SetShown(kb.db.showUI)
+end
+
+--- Returns conflicting assignment and binding profiles for use in displaying confirmations
+kb.IsCommandBound = function(self, command)
+  local isAssigned, assignedBy = false, kb.db.bindMode
+  local isBound, boundBy = false, kb.db.bindMode
+  command = command or self.command
+  for i = 1, #kb.orderedProfiles do
+    local profile = kb.orderedProfiles[i]
+    if i ~= kb.db.bindMode then
+
+      if profile.commands[command] then
+        print(' command: ', i , kb.configHeaders[i], profile.commands[command])
+        isAssigned = true
+        assignedBy = i
+      end
+      if profile.bound[command] then
+        print(' bound: ', i , kb.configHeaders[i], profile.bound[command])
+        isBound = true
+        boundBy = i
+      end
+
+
+
+
+      if isAssigned and isBound then
+        print(' hit: ', i , kb.configHeaders[i], profile.commands[command], profile.bound[command])
+        break
+      end
+    end
+
+  end
+
+  print('|cFFFFFF00IsCommandBound:|r', command,'|r [profile:', kb.db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
+  return isAssigned, isBound, assignedBy, boundBy
+end
+
+local talentSpellHardCodes = {
+  [109248] = 'Binding Shot',
+}
+
+--- Returns a value for use with Texture:SetDesaturated()
+kb.BindingIsLocked = function(key)
+  local success = false
+  for i = 1, db.bindMode-1 do
+    local tier = kb.orderedProfiles[i]
+    if tier.bindings[key] then
+      success = true
+      break
+    end
+  end
+  return success
+end
+
+--- Translates GetBindingKey() results into a printable string.
+kb.BindingString = function(...)
+  local stack = {}
+  for i = 1, select('#', ...) do
+    local key = select(i, ...)
+    if type(key) == 'string' then
+    stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
+      end
+  end
+
+  if #stack >= 1 then
+    return concat(stack, ',')
+  else
+    return nil
+  end
+end
+
+
+function kb:print(...)
+
+  local msg = '|cFF0088FFSkeletonKey|r:'
+  for i = 1, select('#', ...) do
+    msg = msg .. ' ' .. tostring(select(i, ...))
+  end
+  DEFAULT_CHAT_FRAME:AddMessage(msg)
+end
+
+
+kb.Command = function(args, editor)
+  if args:match("import") then
+    kb.ImportCommmit(args)
+    return
+  elseif args:match("scan") then
+    kb.ImportScan(args)
+    SkeletonKey:Update()
+    return
+  elseif args:match("load") then
+    kb:ApplyAllBindings()
+    return
+  end
+
+  if db.showUI then
+    db.showUI = false
+  else
+    db.showUI = true
+    if not InCombatLockdown() then
+      kb:print(L('Config frame opened.'))
+    else
+      kb:print(L('Config frame will open upon exiting combat.'))
+    end
+  end
+  SkeletonKey:SetShown(db.showUI)
+  SkeletonKey:Update(true)
+end
+
+kb.InitProfile = function(profile, prototype)
+  print('|cFF00FFFFkb.InitProfile()', profile, prototype)
+  if not profile then
+    profile = {}
+  end
+  if prototype then
+    for k,v in pairs(prototype) do
+      if not profile[k] then
+        profile[k] = v
+      end
+    end
+  end
+
+  profile.bound = profile.bound or {}
+  profile.buttons = profile.buttons or {}
+  profile.commands = profile.commands or {}
+  profile.bindings = profile.bindings or {}
+  profile.macros = profile.macros or {}
+  profile.talents = profile.talents or {}
+  return profile
+end
+
+kb.ResetProfile = function(profile, prototype)
+  if profile == kb.currentProfile then
+    for i, button in pairs(kb.buttons) do
+      kb.ReleaseSlot(button)
+    end
+  end
+  wipe(profile)
+  kb.InitProfile(profile, prototype)
+end
+
+
+
+--- Handles constructing spec profiles as they are selected
+
+
+--- Obtains profile data or creates the necessary tables
+kb.SelectProfileSet = function(name)
+
+  local defaultMode
+  --- General info
+  classHeader, className, classID = UnitClass('player')
+  print('|cFF00FF00profile:|r', name)
+  print('|cFF00FF00class:|r', UnitClass('player'))
+
+  defaultMode = BINDING_TYPE_GLOBAL
+  if db[name] then
+    defaultMode = BINDING_TYPE_CHARACTER
+    if db[name][kb.specInfo.id] then
+      defaultMode = BINDING_TYPE_SPECIALIZATION
+    end
+  end
+
+  db[name] = kb.InitProfile(db[name],
+    {
+      classHeader = classHeader,
+      className = className,
+      classID = classID
+    })
+  db[name][kb.specInfo.id] = kb.InitProfile(db[name][kb.specInfo.id],
+    {
+      specID = kb.specInfo.id,
+      specName = kb.specInfo.name
+    })
+
+  kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
+  kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
+  kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] = db[name][kb.specInfo.id]
+  kb.orderedProfiles = {db, db[name], db[name][kb.specInfo.id]}
+
+  if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then
+    print('fixing bad bindMode value, was', db.bindMode)
+    db.bindMode = defaultMode
+  end
+
+
+  print(BINDING_TYPE_GLOBAL)
+  kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
+  kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
+  kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name or '')
+
+
+  setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
+  setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
+  setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
+
+  print('|cFF00FF00bindMode:|r', db.bindMode)
+  kb.currentProfile = kb.loadedProfiles[db.bindMode]
+  kb.currentHeader = kb.configHeaders[db.bindMode]
+end
+
+
+function SkeletonKeyMixin:SetTab (id)
+   self.scrollCache[db.bindMode] = kb.scrollOffset
+  db.bindMode =id
+  kb.currentProfile = kb.loadedProfiles[id]
+  kb.currentHeader = kb.configHeaders[db.bindMode]
+  kb.scrollOffset = self.scrollCache[db.bindMode] or 0
+  self:Update(true)
+end
+
+kb.ConfirmBindings = function()
+  kb.ApplyAllBindings()
+  if #kb.pendingAttributes == 0 then
+    kb:print(L("Manual bindings update finished."))
+  else
+    kb:print(L("Manual update will complete upon exiting combat."))
+  end
+  SkeletonKey:Update()
+end
+
+function SkeletonKeyMixin:OnLoad()
+  kb.frame = self
+  print('|cFF0088FF'..self:GetName()..':OnLoad()')
+
+  self.CloseButton:SetScript('OnClick', CloseButton_OnClick)
+  self:RegisterEvent('PLAYER_ENTERING_WORLD')
+  self:RegisterEvent('ADDON_LOADED')
+  self:EnableKeyboard(false)
+
+  self.zoomScale = self:GetScale()
+  self.backdrop = self:GetBackdrop()
+  self.backdropColor = {self:GetBackdropColor() }
+  self.backdropBorder = {self:GetBackdropBorderColor() }
+
+end
+
+function SkeletonKeyMixin:OnEvent(event, arg)
+  if event == 'ADDON_LOADED' then
+
+    print('|cFF00FFFF'..event ..'|r', arg or '', IsLoggedIn())
+        if IsLoggedIn() and not self.initialized then
+      self:Setup()
+      self.initialized = true
+      self:Update()
+    end
+
+
+  elseif kb[event] then
+    if self.initialized then
+      print('|cFF0088FF'..event ..'|r', arg or '')
+      kb[event](self, event, arg)
+    else
+
+      print('|cFF004488'..event ..'|r', arg or '')
+    end
+  end
+end
+
+
+--- post ADDON_LOADED
+function SkeletonKeyMixin:Setup ()
+  print('|cFF00FFFF'..self:GetName()..':Setup()')
+  SkeletonKeyDB = kb.InitProfile(SkeletonKeyDB, {})
+  kb.db = _G.SkeletonKeyDB
+  kb.playerName = UnitName('player')
+  kb.playerRealm = SelectedRealmName()
+  kb.profileName = kb.playerRealm .. '_' .. kb.playerName
+  db = kb.db
+
+  kb.UpdateSpecInfo()
+  kb.UpdateTalentInfo()
+  kb.SelectProfileSet(kb.profileName)
+  -- todo: redo import checking
+
+  kb.UpdateSystemBinds()
+  kb.ApplyAllBindings()
+
+  if not InCombatLockdown() then
+    kb.CreateHooks()
+  else
+    kb:print('Some functionality will not load until breaking combat.')
+    tinsert(kb.pendingCalls, kb.CreateHooks)
+  end
+  SLASH_SKB1 = "/skb"
+  SLASH_SKB2 = "/skeletonkey"
+  SlashCmdList.SKB = kb.Command
+
+  self:SetShown(kb.db.showUI)
+  self:Update(true)
+
+  self:RegisterEvent('UPDATE_MACROS')
+  self:RegisterEvent('UPDATE_BINDINGS')
+  self:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player', 'pet')
+  self:RegisterUnitEvent('PLAYER_SPECIALIZATION_CHANGED', 'player', 'pet')
+  self:RegisterUnitEvent('SPELLS_CHANGED')
+  self:RegisterUnitEvent('TALENT_UPDATE', 'player', 'pet')
+  self:RegisterEvent('PLAYER_REGEN_DISABLED')
+  self:RegisterEvent('PLAYER_REGEN_ENABLED')
+
+  self:RegisterForDrag('LeftButton')
+  self:SetMovable(true)
+  for index, frame in ipairs(self.Plugins) do
+    frame:Setup()
+  end
+end
+
+
+-- Volatiles Access
+kb.FormatActionID = function(actionType, actionID) return tostring(actionType) .. '_' .. tostring(actionID) end
+kb.GetBindings = function() return kb.bindings end
+kb.GetButtons = function() return kb.buttons end
+kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
+kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
+kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SkeletonKey.toc	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,13 @@
+## Interface: 70100
+## Title: SkeletonKey
+## Notes: Key Bindings for dinosaurs
+## Author: Krakyn
+## Version: 1.0-@project-revision@
+## SavedVariables: SkeletonKeyDB
+## X-Category: Interface Enhancements
+## DefaultState: Enabled
+## LoadOnDemand: 0
+## OptionalDeps: Devian
+
+SkeletonKey.xml
+SystemBindings.xml
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SkeletonKey.xml	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,422 @@
+<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="SkeletonKey.lua" />
+  <Script file="ActionTemplates.lua" />
+  <Script file="BindingsFrame.lua" />
+  <Script file="Events.lua" />
+  <Script file="KeyButton.lua" />
+  <Script file="HotKey.lua" />
+
+  <Font name="SkeletonKeyHeaderFont" height="18" font="Fonts\skurri.ttf" outline="NORMAL" justifyH="LEFT" justifyV="TOP" virtual="true" />
+  <Font name="SkeletonKeyButtonFont" justifyH="LEFT" virtual="true"  inherits="NumberFontNormal"  />
+  <Font name="SkeletonKeyMacroFont" height="12" font="Fonts\ARIALN.TTF" justifyH="LEFT" virtual="true" >
+    <Color a="1" r="1" g="1" b="1" />
+  </Font>
+
+  <Font name="SkeletonKeyStatusFont" height="12" font="Fonts\ARIALN.TTF" justifyH="LEFT" virtual="true" />
+
+  <Frame name="SkeletonKey" parent="UIParent" hidden="true" clampedToScreen="true" movable="true" enableMouse="true" toplevel="true" mixin="SkeletonKeyMixin">
+    <Anchors>
+      <Anchor point="TOP" y="-25" x="0" />
+    </Anchors>
+    <Scripts>
+      <OnLoad method="OnLoad" />
+      <OnEvent method="OnEvent" />
+      <OnShow method="OnShow" />
+      <OnUpdate method="OnUpdate" />
+      <OnKeyDown method="OnKeyDown" />
+      <OnKeyUp method="OnKeyUp" />
+      <OnMouseDown method="OnMouseDown" />
+      <OnMouseUp method="OnMouseUp" />
+      <OnMouseWheel method="OnMouseWheel" />
+      <OnDragStart method="OnDragStart" />
+      <OnDragStop method="OnDragStop" />
+    </Scripts>
+    <Backdrop bgFile="Interface\Tooltips\UI-Tooltip-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
+      <EdgeSize val="16" />
+      <TileSize val="16" />
+      <BorderColor a="1" r="1" g="1" b="1" />
+      <BackgroundInsets top="4" left="4" bottom="4" right="4" />
+      <Color r="0" g="0" b="0" a="1" />
+    </Backdrop>
+    <Size x="600" y="200" />
+    <Layers>
+      <Layer level="BACKGROUND">
+
+        <Texture parentKey="info">
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+            <Anchor point="RIGHT" />
+          </Anchors>
+          <Size y="42" />
+        </Texture>
+
+        <Texture parentKey="headerbg" alphaMode="MOD">
+          <Size y="32" />
+          <Anchors>
+            <Anchor point="TOPLEFT" x="2" y="-2" />
+            <Anchor point="RIGHT" x="-2" />
+          </Anchors>
+          <Color a="1" r="1" g="1" b="1" />
+          <Gradient orientation="VERTICAL">
+            <MinColor r="0" g="0" b="0"/>
+            <MaxColor r="1" g="1" b="1"/>
+          </Gradient>
+        </Texture>
+
+
+
+        <Texture parentKey="profilebg">
+          <Size  y="102" />
+          <Anchors>
+            <Anchor point="TOP" relativePoint="BOTTOM" relativeKey="$parent.headerbg" />
+            <Anchor point="LEFT" x="2" y="0" />
+            <Anchor point="RIGHT" x="-2" y="0" />
+          </Anchors>
+          <Color a="1" r="0" g="0" b="0" />
+        </Texture>
+
+        <Texture parentKey="bg">
+          <Size y="4" />
+          <Anchors>
+            <Anchor point="TOP" relativePoint="BOTTOM" relativeKey="$parent.profilebg" />
+            <Anchor point="LEFT" x="4" y="0" />
+          </Anchors>
+          <Color a="1" r="0" g="0" b="0" />
+        </Texture>
+
+        <Texture parentKey="footer">
+          <Size y="52" />
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeKey="$parent.bg" x="0" y="0" />
+            <Anchor point="RIGHT" x="-2" />
+          </Anchors>
+          <Color a="1" r="0" g="0" b="0" />
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+
+        <FontString inherits="SkeletonKeyHeaderFont" text="Bindings" parentKey="header">
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+          </Anchors>
+        </FontString>
+
+        <FontString parentKey="profiletext" inherits="SkeletonKeyHeaderFont" justifyH="LEFT" text="Foobar">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT" relativeKey="$parent.header" x="8" y="5" />
+          </Anchors>
+          <Color a="1" r="1" g="0.7" b="0" />
+        </FontString>
+
+        <FontString parentKey="statustext" inherits="SkeletonKeyStatusFont" justifyH="LEFT" justifyV="TOP">
+          <Anchors>
+            <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" relativeKey="$parent.bg" x="12" y="7" />
+          </Anchors>
+        </FontString>
+
+
+        <FontString parentKey="bindingstext" inherits="NumberFont_Outline_Large" justifyH="RIGHT" justifyV="TOP">
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" relativeKey="$parent.bg" x="-12" y="7" />
+          </Anchors>
+          <Color a="1" r="0" g="1" b="0" />
+        </FontString>
+
+      </Layer>
+    </Layers>
+    <Frames>
+
+
+      <Button parentKey="macroButton" name="$parentMacro" inherits="SecureActionButtonTemplate">
+        <Attributes>
+          <Attribute name="*type*" value="macro" />
+        </Attributes>
+      </Button>
+      <Button parentKey="actionButton" name="$parentKey" inherits="SecureActionButtonTemplate" />
+
+      <Frame name="$parentHotKeyText" mixin="SkeletonHotKeyMixin" parentArray="Plugins">
+        <Scripts>
+          <OnLoad method="OnLoad" />
+          <OnEvent method="OnEvent" />
+        </Scripts>
+      </Frame>
+
+      <Frame name="KeyBinderTutorial" parent="KeyBinder">
+
+        <Size x="20" y="20" />
+        <Anchors>
+          <Anchor point="TOPLEFT" relativeKey="$parent.footer" x="2" y="-2" />
+          <Anchor point="TOPRIGHT" relativeKey="$parent.footer" x="-2" y="-2" />
+        </Anchors>
+        <Layers>
+          <Layer level="ARTWORK">
+            <Texture name="$parentMouseLeftClick" file="Interface\TutorialFrame\UI-TUTORIAL-FRAME">
+              <Size x="15" y="20"/>
+              <Anchors>
+                <Anchor point="TOPLEFT" />
+              </Anchors>
+              <TexCoords left="0.0019531" right="0.1484375" top="0.4257813" bottom="0.6210938"/>
+            </Texture>
+            <FontString inherits="KTLogString" text="to change a key binding.">
+              <Anchors>
+                <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeTo="$parentMouseLeftClick" x="2" y="0" />
+              </Anchors>
+            </FontString>
+
+            <Texture name="$parentMouseRightClick" file="Interface\TutorialFrame\UI-TUTORIAL-FRAME">
+              <Size x="15" y="20"/>
+              <Anchors>
+                <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeTo="$parentMouseLeftClick" x="0" y="-2" />
+              </Anchors>
+              <TexCoords left="0.0019531" right="0.1484375" top="0.6269531" bottom="0.8222656"/>
+            </Texture>
+            <FontString inherits="KTLogString" text="to remove actions from profile.">
+              <Anchors>
+                <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeTo="$parentMouseRightClick" x="2" y="0" />
+              </Anchors>
+            </FontString>
+
+          </Layer>
+        </Layers>
+      </Frame>
+
+      <Button inherits="UIPanelCloseButton" parentKey="CloseButton">
+        <Anchors>
+          <Anchor point="TOPRIGHT" />
+        </Anchors>
+      </Button>
+
+
+      <CheckButton name="KeyBinderStickyMode" inherits="UICheckButtonTemplate">
+        <Size  y="32" x="32" />
+        <Layers>
+          <Layer level="OVERLAY">
+            <FontString parentKey="header" inherits="KTLogString" text="Sticky Mode:" />
+          </Layer>
+        </Layers>
+      </CheckButton>
+
+      <CheckButton name="KeyBinderHoverInput" inherits="UICheckButtonTemplate">
+        <Size  y="32" x="32" />
+        <Anchors>
+          <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" x="-4" y="0" relativeTo="KeyBinderStickyMode" />
+        </Anchors>
+        <Layers>
+          <Layer level="OVERLAY">
+            <FontString parentKey="header" inherits="KTLogString" text="Bind" />
+            <FontString inherits="SkeletonKeyHeaderFont" text="Settings">
+              <Anchors>
+                <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" x="0" y="32" />
+              </Anchors>
+            </FontString>
+          </Layer>
+        </Layers>
+      </CheckButton>
+
+      <CheckButton name="KeyBinderProtectBindings" inherits="UICheckButtonTemplate">
+        <Size  y="32" x="32" />
+        <Anchors>
+          <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" x="-4" y="0" relativeTo="KeyBinderHoverInput" />
+        </Anchors>
+        <Layers>
+          <Layer level="OVERLAY">
+            <FontString parentKey="header" inherits="KTLogString" text="Safe" />
+          </Layer>
+        </Layers>
+      </CheckButton>
+    </Frames>
+  </Frame>
+
+
+  <CheckButton name="KeyButton" virtual="true" mixin="SkeletonKeyButtonMixin">
+    <Size x="32" y="32" />
+    <Scripts>
+      <OnLoad method="OnLoad" />
+      <OnClick method="OnClick" />
+      <OnUpdate method="OnUpdate" />
+      <OnKeyDown method="OnKeyDown" />
+      <OnDragStart method="OnDragStart" />
+      <OnReceiveDrag method="OnReceiveDrag" />
+      <OnKeyUp method="OnKeyUp" />
+      <OnEnter method="OnEnter" />
+      <OnLeave method="OnLeave" />
+    </Scripts>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="border" setAllPoints="true">
+          <Color a="1" r=".25" g=".25" b=".25" />
+        </Texture>
+      </Layer>
+      <Layer level="BORDER">
+
+        <Texture parentKey="bg">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="2" y="-2" />
+            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
+          </Anchors>
+          <Color a="0.5" r="0" g="0" b="0" />
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture setAllPoints="true" parentKey="icon">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="2" y="-2" />
+            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
+          </Anchors>
+
+          <TexCoords left="0.1" right="0.9" top="0.1" bottom="0.9" />
+
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString inherits="NumberFontNormal" parentKey="header" wordwrap="false" justifyH="LEFT">
+
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="2" y="-2" />
+            <Anchor point="RIGHT" x="128" y="0" />
+          </Anchors>
+        </FontString>
+        <FontString inherits="NumberFontNormal" parentKey="bind">
+          <Anchors>
+            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
+          </Anchors>
+        </FontString>
+        <FontString inherits="SkeletonKeyMacroFont" parentKey="macro">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="2" y="-2" />
+            <Anchor point="RIGHT" x="-2" y="0" />
+          </Anchors>
+        </FontString>
+        <FontString inherits="NumberFontNormal" parentKey="details" justifyH="LEFT">
+          <Anchors>
+            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" x="0" y="-2" relativeKey="$parent.header" />
+            <Anchor point="RIGHT" x="128" y="0" />
+          </Anchors>
+        </FontString>
+
+        <Texture parentKey="ignoreTexture" file="Interface\PaperDollInfoFrame\UI-GearManager-LeaveItem-Transparent" hidden="true">
+          <Anchors>
+            <Anchor point="TOPLEFT" x="0" y="0" />
+            <Anchor point="BOTTOMRIGHT" x="0" y="0" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+    <HighlightTexture alphaMode="ADD">
+      <Anchors>
+        <Anchor point="TOPLEFT" x="2" y="-2" />
+        <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
+      </Anchors>
+      <Color a="1" r="0.15" g="0.15" b="0.15" />
+    </HighlightTexture>
+    <Frames>
+      <Frame parentKey="alert" hidden="true">
+        <Size x="18" y="18" />
+        <Anchors>
+          <Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMLEFT" relativeKey="$parent.bind" x="-2" />
+        </Anchors>
+        <Layers>
+          <Layer level="ARTWORK">
+            <Texture file="Interface\DialogFrame\UI-Dialog-Icon-AlertNew" setAllPoints="true" />
+          </Layer>
+        </Layers>
+      </Frame>
+    </Frames>
+  </CheckButton>
+  <Frame name="SkeletonKeyPanelTemplate"  parent="SkeletonKey" hidden="true" parentArray="Panels" virtual="true">
+    <Scripts>
+      <OnLoad method="OnLoad" />
+      <OnShow method="OnShow" />
+      <OnEvent method="OnEvent" />
+    </Scripts>
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="bg" />
+      </Layer>
+    </Layers>
+  </Frame>
+
+  <Frame name="$parentActionList" mixin="SkeletonKeyActionListMixin" inherits="SkeletonKeyPanelTemplate">
+    <Layers>
+      <Layer level="BORDER">
+
+
+        <Texture parentKey="profileStripe">
+          <Size y="4" />
+          <Anchors>
+
+            <Anchor point="TOP" />
+            <Anchor point="LEFT" x="0" y="0" />
+            <Anchor point="RIGHT" x="0" y="0" />
+          </Anchors>
+        </Texture>
+      </Layer>
+    </Layers>
+    <Frames>
+      <Button parentKey="UnbindButton" hidden="true">
+        <Size x="48" y="24" />
+        <Layers>
+          <Layer level="BACKGROUND">
+            <Texture parentKey="Background">
+              <Anchors>
+                <Anchor point="TOPLEFT" x="-4" y="52" />
+                <Anchor point="BOTTOMRIGHT" x="124" y="-4" />
+              </Anchors>
+              <Color a="0.9" r="0" g="0" b="0" />
+            </Texture>
+          </Layer>
+          <Layer level="OVERLAY">
+
+            <FontString parentKey="savingText" inherits="SkeletonKeyHeaderFont" text="Press a key.">
+                <Anchors>
+                  <Anchor point="TOP" relativePoint="BOTTOM" y="-3" />
+                </Anchors>
+              </FontString>
+            <FontString inherits="SkeletonKeyButtonFont" text="Clear">
+              <Anchors>
+                <Anchor point="CENTER" />
+              </Anchors>
+            </FontString>
+          </Layer>
+        </Layers>
+        <NormalTexture>
+          <Color a="1" r=".7" g="0.12" b=".06" />
+        </NormalTexture>
+        <HighlightTexture alphaMode="ADD">
+
+          <Color a="1" r=".15" g="0.15" b=".15" />
+        </HighlightTexture>
+      </Button>
+    </Frames>
+  </Frame>
+  <Frame name="$parentSystemBindings" mixin="SkeletonKeySystemBindingsMixin" inherits="SkeletonKeyPanelTemplate" />
+
+  <Button name="SkeletonKeyTabTemplate" parentArray="tabButtons" virtual="true">
+    <Size x="48" y="32" />
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="Background">
+          <Color a="1" r="0.1" g="0.1" b="0.1" />
+        </Texture>
+      </Layer>
+      <Layer level="ARTWORK">
+        <Texture parentKey="Icon">
+          <Size x="28" y="28" />
+          <Anchors>
+            <Anchor point="TOPLEFT" />
+          </Anchors>
+        </Texture>
+      </Layer>
+      <Layer level="OVERLAY">
+        <FontString parentKey="Label" inherits="GameFontNormal">
+          <Anchors>
+            <Anchor point="LEFT" relativePoint="RIGHT" relativeKey="$parent.Icon" x="3" y="0" />
+          </Anchors>
+        </FontString>
+      </Layer>
+    </Layers>
+  </Button>
+
+</Ui>
\ No newline at end of file
--- a/SkeletonKey/ActionTemplates.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,509 +0,0 @@
--- SkeletonKey
--- ActionTemplates.lua
--- Created: 7/29/2016 9:14 PM
--- %file-revision%
--- Code dealing with the implementation of action hotkeys
-
-local tostring, tonumber, pairs, ipairs = tostring, tonumber, pairs, ipairs
-local unpack, SetBinding = unpack, SetBinding
-local tinsert, tContains, select, wipe =  tinsert, tContains, select, table.wipe
-local GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo = GetSpellBookItemInfo, GetSpellBookItemName, GetSpellInfo
-local GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell = GetSpecialization, GetSpecializationInfo, IsPassiveSpell, IsTalentSpell
-local PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells = PetHasSpellbook, PetHasActionBar, GetPetActionInfo, HasPetSpells
-local GetProfessions, GetProfessionInfo, GetTalentInfo = GetProfessions, GetProfessionInfo, GetTalentInfo
-local GetNumBindings, GetBinding = GetNumBindings, GetBinding
-
-local kb, print, wrap = LibStub('LibKraken').register(KeyBinder, 'Info')
-local cprint = (DEVIAN_PNAME == 'SkeletonKey') and function(...) _G.print('Cfg', ...) end or function() end
-
-local CLICK_KEYBINDER_MACRO = "CLICK KeyBinderMacro:"
-local CLICK_KEYBINDER_KEY = "CLICK KeyBinderKey:"
-local PET_BASIC_SUBTEXT = 'Basic Attack'
-local PET_SPECIAL_SUBTEXT = 'Special Ability'
-local PETACTION_SCRIPT = {
-  [PET_ACTION_MOVE_TO] = {'pet_move_to', SLASH_PET_MOVE_TO1},
-  [PET_ACTION_ATTACK] = {'pet_attack', SLASH_PET_ATTACK1},
-  [PET_ACTION_FOLLOW] = {'pet_follow', SLASH_PET_FOLLOW1},
-  [PET_ACTION_WAIT] = {'pet_stay', SLASH_PET_STAY1 },
-  [PET_MODE_AGGRESSIVE] = {'pet_aggressive', SLASH_PET_AGGRESSIVE1 },
-  [PET_MODE_DEFENSIVE] = { 'pet_defensive', SLASH_PET_DEFENSIVE1},
-  [PET_MODE_PASSIVE] = { 'pet_passive', SLASH_PET_PASSIVE1},
-  [PET_MODE_ASSIST] = {'pet_assist', SLASH_PET_ASSIST1},
-}
-local SECONDARY_PROFESSIONS = {
-  [5] = 3,
-  [7] = 4,
-  [9] = 5,
-  [10] = 6
-}
-local petSpellCache,petSubtextCache
-local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
-
---kb.ChangedBindings = {}
---kb.ActionTypes = {}
-
-local atype = kb.ActionTypes
-
---- Caps Lock
-atype['mount'] = function(id, name)
-  if id == SUMMON_RANDOM_FAVORITE_MOUNT_SPELL then
-    return CLICK_KEYBINDER_MACRO, 'mount_random', "/script C_MountJournal.SummonByID(0)"
-  else
-    return CLICK_KEYBINDER_MACRO, 'mount_'..id, "/script C_MountJournal.SummonByID("..id..")"
-  end
-end
-
-atype['macro'] = function(id, name)
-  return CLICK_KEYBINDER_MACRO, 'macro_' .. tostring(name), id
-end
-
-atype['equipset'] = function(id, name)
-    return CLICK_KEYBINDER_MACRO, 'equipset_'..tostring(name), "/script UseEquipmentSet("..tostring(id)..")"
-end
-
-atype['spell'] = function(id, name)
-  local attributeName = name
-  if kb.ProfessionCache[id] then
-    attributeName = "profession_".. kb.ProfessionCache[id].profOffset .. '_' .. kb.ProfessionCache[id].spellNum
-  end
-  return CLICK_KEYBINDER_KEY, attributeName, name
-end
-
-atype['petaction'] = function(_, name)
-  -- ID doesn't exist for basic commands, even though they can be picked up
-  local attributeName, attributeValue = "petaction_" .. tostring(name), "/cast "..tostring(name)
-
-  if not petSpellCache then
-    kb.UpdatePetInfo()
-  end
-  -- Compose a multi-macro for subtext abilities
-  if petSpellCache[name] then
-    attributeValue = ""
-    for spellName, enabled in pairs(petSubtextCache[petSpellCache[name]]) do
-      attributeValue = attributeValue .. "/cast " .. spellName .. "\n"
-    end
-  end
-
-  if PETACTION_SCRIPT[name] then
-    attributeName, attributeValue = unpack(PETACTION_SCRIPT[name])
-  elseif kb.PetCache.special[name] then
-    attributeName = "petaction_"..kb.PetCache.special[name][3].."_" .. tonumber(kb.PetCache.special[name][6])
-  end
-  return CLICK_KEYBINDER_MACRO, attributeName, attributeValue
-end
-
-atype['battlepet'] = function(id, name)
-  return CLICK_KEYBINDER_MACRO, 'battlepet_' .. tostring(name), SLASH_SUMMON_BATTLE_PET1 .. " " .. tostring(name)
-end
-
-atype['item'] = function(id, name)
-  return CLICK_KEYBINDER_KEY, tostring(name), id
-end
-
-
---- Resolves the SecureActionButton attribute names used for the given action
-kb.RegisterAction = function(actionType, id, name)
-  assert(atype[actionType], 'Missing actionType handler for `'..tostring(actionType)..'`')
-  local target, attributeName, attributeValue  = atype[actionType](id, name)
-  local command = target .. attributeName
-  kb.macros[attributeName] = {attributeValue, command}
-  return attributeName, attributeValue, command
-end
-
-
-
-
-kb.ApplyTalentBinding = function(talentInfo, cache)
-  for i = 5, #talentInfo do
-    local command = CLICK_KEYBINDER_KEY.. talentInfo[2]
-    SetBinding(talentInfo[i], command)
-    --cprint(' **', talentInfo[i], '->', command)
-    tinsert(cache, talentInfo[i])
-  end
-end
-kb.CacheTalentBinding = function(talentInfo, cache)
-
-  local spellID = talentInfo[4]
-  cache[spellID] = cache[spellID] or {}
-  cache[spellID] = {select(5,unpack(talentInfo)) }
-  --cprint(spellID, unpack(kb.TalentBindings[spellID]))
-end
-
-
-do
-  local commandActions = {}
-  local bindings = kb.bindings
-  local key, macro = KeyBinderKey, KeyBinderMacro
-  kb.LoadBinding = function(command, name, icon, actionType, actionID, macroName, macroText )
-
-    local indexKey = actionType..'_'..actionID
-    local buttonTypeSuffix, buttonType = name, actionType
-    local actionKey, actionValue = "*"..actionType.."-"..name, actionID
-    local button = key
-    if actionType == 'spell' then
-      buttonTypeSuffix = name
-      buttonType = actionType
-      actionValue = name
-    elseif actionType == 'item' then
-      buttonTypeSuffix = name
-      buttonType = actionType
-      actionValue = name
-    else
-      button = macro
-      buttonTypeSuffix = macroName
-      buttonType = 'macro'
-      if actionType == 'macro' then
-        actionKey = "*macro-" .. macroName
-        actionValue = name
-      else
-        actionKey = '*macrotext-'..macroName
-        actionValue = macroText
-      end
-    end
-
-    kb.SecureAttribute(button, "*type-"..buttonTypeSuffix, buttonType)
-    kb.SecureAttribute(button, actionKey, actionValue)
-
-    kb.bindings[indexKey] = kb.bindings[indexKey] or {}
-    kb.bindings[command] = kb.bindings[indexKey]
-    commandActions[command] = indexKey
-    return bindings[indexKey], actionID
-  end
-
-  kb.ApplyBindings = function (profile)
-    --cprint('binding profile', profile)
-
-    for slot, data in pairs(profile.buttons) do
-      local bindsTable, actionID = kb.LoadBinding(unpack(data))
-      local command = data[1]
-    end
-
-    for key, command in pairs(profile.bindings) do
-      cprint('|cFF00FFFF'.. key .. '|r to|cFF00FF00', command, commandActions[command])
-      SetBinding(key, command)
-      if kb.bindings[command] and not tContains(kb.bindings[command], key) then
-        tinsert(kb.bindings[command], key)
-      end
-    end
-
-    for spellName, talentInfo in pairs(profile.talents) do
-      local dummy = GetSpellInfo(spellName)
-      local func = kb.CacheTalentBinding
-      local dest = kb.TalentBindings
-      if dummy then
-        --cprint('|cFFBBFF00Active:|r', dummy)
-        local macroName, spellName, actionType, actionID = unpack(talentInfo)
-        local indexKey = actionType .. '_' .. actionID
-        kb.bindings[indexKey] = {}
-        func = kb.ApplyTalentBinding
-        dest = kb.bindings[indexKey]
-      else
-
-        --cprint('|cFFFF4400Inactive:|r', talentInfo[2])
-      end
-      func(talentInfo, dest)
-    end
-
-  end
-
-  kb.ApplyAllBindings =function ()
-    wipe(kb.TalentBindings)
-    wipe(kb.bindings)
-    --kb:print('Loading binding profile', kb.profileName)
-
-    -- reflect action key settings
-    if GetCVarBool("ActionButtonUseKeyDown") then
-      KeyBinderMacro:RegisterForClicks("AnyDown")
-      KeyBinderKey:RegisterForClicks("AnyDown")
-    else
-      KeyBinderMacro:RegisterForClicks("AnyUp")
-      KeyBinderKey:RegisterForClicks("AnyUp")
-    end
-
-    for i, profile in ipairs(kb.orderedProfiles) do
-      kb.ApplyBindings(profile)
-    end
-    -- do this after to ensure that profession binds are properly overridden
-    kb.UpdateProfessionInfo()
-
-    SaveBindings(GetCurrentBindingSet())
-  end
-end
-
-
-kb.specInfo = {}
-kb.UpdateSpecInfo = function()
-  kb.specInfo.id = GetSpecialization()
-  kb.specInfo.globalID, kb.specInfo.name, kb.specInfo.desc, kb.specInfo.texture = GetSpecializationInfo(kb.specInfo.id)
-end
-
-kb.UpdateMacroInfo =  function()
-  for index = 1, GetNumMacros() do
-    local name = GetMacroInfo(index)
-    kb.SecureAttribute(KeyBinderMacro, "*type-macro_"..tostring(name), 'macro')
-    kb.SecureAttribute(KeyBinderMacro, "*macro-macro_"..tostring(name), index)
-  end
-end
-
-kb.UpdateTalentInfo = function()
-  if kb.talentsPushed then
-    return
-  end
-  wipe(kb.TalentCache)
-  for row =1, MAX_TALENT_TIERS do
-    for col = 1, NUM_TALENT_COLUMNS do
-      local talentID, talentName, icon, selected, available, spellID = GetTalentInfo(row, col, 1)
-      local talentInfo = kb.TalentCache[spellID] or {}
-      talentInfo.row = 1
-      talentInfo.col = col
-      talentInfo.name = talentName
-      talentInfo.talentID = talentID
-      talentInfo.selected = selected
-      talentInfo.available = available
-      talentInfo.spellID = spellID
-      kb.TalentCache[spellID] = talentInfo
-      kb.TalentCache[talentName] = talentInfo
-      --print('Talent ', row, col, spellID, talentName)
-    end
-  end
-  kb.talentsPushed = true
-
-  kb.UpdateDynamicButtons('talent')
-end
-
-kb.UpdateProfessionInfo = function()
-  wipe(kb.ProfessionCache)
-  local profs = {GetProfessions() }
-  --print(GetProfessions())
-  local primaryNum = 0
-  for i = 1, 6 do
-    if profs[i] then
-      local index = profs[i]
-      local profName, texture, _, _, numSpells, spellOffset = GetProfessionInfo(index)
-      --print(i, index, profName, numSpells, spellOffset)
-      if not SECONDARY_PROFESSIONS[index] then
-        primaryNum = primaryNum + 1
-      end
-      local profNum = SECONDARY_PROFESSIONS[index] or primaryNum
-      --print(i, profNum)
-
-
-      kb.ProfessionCache[profNum] = kb.ProfessionCache[profNum] or {}
-
-      for j = 1, numSpells do
-        local spellName, _, icon, _, _, _, spellID = GetSpellInfo(spellOffset+j, BOOKTYPE_PROFESSION)
-
-        local profInfo = {
-          spellName = spellName,
-          spellID = spellID,
-          icon = icon,
-          profOffset = i,
-          profIndex = index,
-          spellOffset = (spellOffset+j),
-          spellNum = j
-        }
-
-        kb.SecureAttribute(KeyBinderKey, "*type-profession_"..i .. '_' ..j, "spell")
-        kb.SecureAttribute(KeyBinderKey, "*spell-profession_"..i .. '_' ..j, spellName)
-
-        kb.ProfessionCache[i .. '_' .. j] = profInfo
-        kb.ProfessionCache[spellName] = profInfo
-        kb.ProfessionCache[spellID] = profInfo
-        --print('  |cFF0088FF['..i..']|r|cFFFF44BB['..spellOffset+i..']|r', spellName, "profession_"..i .. '_' ..j)
-      end
-    end
-
-  end
-
-  kb.UpdateDynamicButtons('profession')
-end
-
-
-
-kb.UpdatePetInfo = function()
-  local hasPetSpells, petType = HasPetSpells()
-
-  -- reconcile saved data if it becomes available
-  if kb.db then
-    kb.db.petSpellsDB = kb.db.petSpellsDB or {}
-    kb.db.petSpellsDB.subtext = kb.db.petSpellsDB.subtext or {}
-    kb.db.petSpellsDB.spell = kb.db.petSpellsDB.spell or {}
-    local spellCache = kb.db.petSpellsDB.spell
-    local subtextCache = kb.db.petSpellsDB.subtext
-    if petSpellCache then
-      for k,v in pairs(petSpellCache) do
-        if not spellCache[k] then
-          spellCache[k] = v
-        end
-      end
-    end
-    petSpellCache = spellCache
-    if petSubtextCache then
-      for k,v in pairs(petSubtextCache) do
-        if not subtextCache[k] then
-          subtextCache[k] = v
-        end
-      end
-    end
-    petSubtextCache = subtextCache
-  else
-    petSpellCache = {}
-    petSubtextCache = {}
-  end
-
-  if PetHasSpellbook() then
-    --print('PET SPELLBOOK')
-    local i = 1
-    local specialNum = {}
-    repeat
-
-      local spellType, spellID = GetSpellBookItemInfo(i, BOOKTYPE_PET)
-      local spellName, subText = GetSpellBookItemName(i, BOOKTYPE_PET)
-      local texture = GetSpellBookItemTexture(i, BOOKTYPE_PET)
-      local isPassive = IsPassiveSpell(i, BOOKTYPE_PET)
-      if not isPassive then
-        if spellName then
-          kb.PetCache.spellslot[spellName] = {i, spellName, subText, spellID, texture}
-          --print('|cFF00FF88spellslot['..spellName..']|r', '=>', i, subText)
-
-          if subText then
-            -- make sure that pet specialization subtext maps correctly
-            --if match(subText, kb.PetCache.specName) then
-            --  subText = 'specialization'
-            --end
-            kb.PetCache.subtext[subText] = kb.PetCache.subtext[subText] or {}
-            specialNum[subText] = (specialNum[subText] or 0) + 1
-
-            petSpellCache[spellName] = subText
-            petSubtextCache[subText] = petSubtextCache[subText] or {}
-
-            -- add to the list
-            if not petSubtextCache[subText][spellName] then
-              petSubtextCache[subText][spellName] = true
-
-              local macrotext = ""
-              for spellName, enabled in pairs(petSubtextCache[subText]) do
-                macrotext = macrotext .. "/cast " .. spellName .. "\n"
-              end
-              kb.SecureAttribute(KeyBinderMacro, "*macrotext-petaction_"..subText.."_"..specialNum[subText], macrotext)
-              --print('|cFF00FFFFspecial['..spellName..']|r', '\n','|cFF00FFFFsubtext['..subText..']['..specialNum[subText]..']|r', '=>', i, spellName, subText, spellID,  texture, specialNum[subText])
-            end
-
-
-
-            local entry = {i, spellName, subText, spellID,  texture, specialNum[subText]}
-
-            kb.PetCache.special[spellName] = entry
-            kb.PetCache.subtext[subText][specialNum[subText]] = entry
-          end
-
-          if spellID then
-            kb.PetCache.spell[i] = {spellID, spellName, subText}
-            --print('|cFF0088FFspell['..i..']|r', '=>', spellID, spellName, subText)
-          end
-        end
-
-
-      end
-
-      i = i + 1
-    until spellType == nil
-  else
-    --print('NO PET SPELLBOOK')
-    wipe(kb.PetCache.spell)
-    wipe(kb.PetCache.spellslot)
-  end
-
-  if PetHasActionBar() then
-    --print('PET ACTION BAR')
-    for i = 1, 10 do
-
-
-      local name, subtext, texture, isToken, isActive = GetPetActionInfo(i)
-      if name then
-        kb.PetCache.action[i] = {name, subtext, texture, isToken, isActive }
-
-
-      end
-      --print('|cFFFFFF00action['..i..']|r', name, subtext, texture)
-    end
-  else
-    --print('NO PET ACTION BAR')
-    wipe(kb.PetCache.action)
-  end
-
-  kb.UpdateDynamicButtons('petaction')
-
-end
-
-kb.UpdateSystemBinds = function()
-  wipe(kb.SystemBindings)
-  local n = GetNumBindings()
-  for i=1, n do
-    local command, key1, key2 = GetBinding(i)
-    if not command:match('ACTION.*%d+') then
-      if key1 then
-        kb.SystemBindings[key1] = command
-      end
-      if key2 then
-        kb.SystemBindings[key2] = command
-      end
-    else
-      --print('ignoring action button binding', command)
-    end
-  end
-end
-
-kb.UpdateDynamicButtons = function(dynamicType)
-  for i, button in ipairs(kb.buttons) do
-    if button.isDynamic == dynamicType then
-      kb.UpdateSlot(button, true)
-    end
-  end
-end
-
-kb.pendingAttributes = {}
-kb.SecureAttribute = function(target, name, value)
-  if InCombatLockdown() then
-    if #kb.pendingAttributes == 0 then
-      kb:print(kb.L('Key bindings will be applied when you exit combat.'))
-    end
-
-    tinsert(kb.pendingAttributes, {target, name, value})
-    kb:RegisterEvent('PLAYER_REGEN_ENABLED')
-
-  else
-
-    cprint('|cFFFF4444' .. target:GetName()..'|r.|cFFFFFF00'.. tostring(name)..'|r = "'..tostring(value)..'"')
-    target:SetAttribute(name, value)
-  end
-end
-
-kb.PLAYER_REGEN_ENABLED = function()
-  if #kb.pendingAttributes >= 1 then
-    local args = tremove(kb.pendingAttributes)
-    while args do
-      local target, name, value = unpack(args)
-      --print(target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"')
-      cprint('deferred', target:GetName(), 'attribute', '"'.. tostring(name)..'" = "'..tostring(value)..'"')
-      target:SetAttribute(name, value)
-      args = tremove(kb.pendingAttributes)
-    end
-  end
-
-  if #kb.pendingCalls >= 1 then
-
-    local func = tremove(kb.pendingCalls)
-    while func do
-      func()
-    end
-  end
-end
-
-kb.UpdateBindingsCache = function(actionType, actionID, bindings)
-  local indexKey = actionType .. '_' .. actionID
-  kb.bindings[indexKey] = bindings
-
-  --print('|cFF00FF00'..indexKey..'|r = {', table.concat(bindings,', '), '}')
-  tinsert(kb.ChangedBindings, {actionType, actionID})
-end
\ No newline at end of file
--- a/SkeletonKey/BindingsFrame.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,699 +0,0 @@
--- KrakTool
--- BindingsFrame.lua
--- Created: 7/28/2016 3:39 PM
--- %file-revision%
--- Handles the arrangement of and interaction with the SkeletonKey frame
---[=[
-  -- some useful texture paths
-  [[Interface\PaperDollInfoFrame\UI-GearManager-Undo]]
-  [[Interface\PetPaperDollFrame\UI-PetHappiness]]
-  [[Interface\RAIDFRAME\ReadyCheck-Waiting]]
-  [[Interface\RAIDFRAME\ReadyCheck-Read]]
-  [[Interface\RAIDFRAME\ReadyCheck-NotReady]]
-  [[Interface\TradeSkillFrame\UI-TradeSkill-LinkButton]]
-  [[Interface\TUTORIALFRAME\UI-TUTORIAL-FRAME]]
-  [[Interface\UI-TutorialFrame-QuestGiver\UI-TutorialFrame-QuestGray]]
---]=]
-
-local kb, print = LibStub("LibKraken").register(KeyBinder, 'BindingsUI')
-local L = kb.L
-local BINDS_PER_ROW = 2
-local BINDING_TYPE_SPECIALIZATION = 3
-local BINDING_TYPE_CHARACTER = 2
-local BINDING_TYPE_GLOBAL = 1
-local BUTTON_HSPACING = 128
-local BUTTON_SPACING = 4
-local BUTTON_PADDING = 12
-local KEY_BUTTON_SIZE = 48
-local NUM_KEY_SLOTS = BINDS_PER_ROW * 8
-local TAB_HEIGHT = 40
-
-local BINDING_SCHEME_COLOR = {
-  [BINDING_TYPE_GLOBAL] = {0,.125,.5,.5},
-  [BINDING_TYPE_CHARACTER] = {0,0.25,0,0.5},
-  [BINDING_TYPE_SPECIALIZATION] = {.25,0,0,0.5},
-}
-local BINDING_SCHEME_VERTEX = {
-  [BINDING_TYPE_GLOBAL] = {0,.5,1,1},
-  [BINDING_TYPE_CHARACTER] = {0,1,0,1},
-  [BINDING_TYPE_SPECIALIZATION] = {1,1,1,1},
-}
-local BINDING_SCHEME_TEXT = {
-  [BINDING_TYPE_SPECIALIZATION] = {0, 1, 1},
-  [BINDING_TYPE_CHARACTER] = {0, 1, 0},
-  [BINDING_TYPE_GLOBAL] = {0, 1, 1}
-}
-
-local match, strupper = string.match, string.upper
-local tremove, tinsert, ipairs, pairs, unpack = table.remove, table.insert, ipairs, pairs, unpack
-local tonumber, tostring = tonumber, tostring
-local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
-local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
-local GetBindingAction, GetBindingKey, GetCurrentBindingSet = GetBindingAction, GetBindingKey, GetCurrentBindingSet
-local SetBinding, SaveBindings = SetBinding, SaveBindings
-local GetSpellInfo, InCombatLockdown = GetSpellInfo, InCombatLockdown
-
-kb.ProcessInput = function(key)
-  if key == 'ESCAPE' then
-    kb.DeactivateSlot(kb.saveTarget)
-    kb.ui()
-    return
-  end
-
-  if (match(key, '[RL]SHIFT') or match(key, '[RL]ALT') or match(key, '[RL]CTRL')) then
-    return
-  end
-
-  if kb.saveTarget then
-    if kb.SaveSlot(kb.saveTarget, key) then
-      if not (kb.db.stickyMode or kb.db.hoverInput) then
-        kb.DeactivateSlot(kb.saveTarget)
-      end
-    end
-    kb.ui()
-  end
-end
-
-local lastFolder
-local restingAlpha = 0.7
-local fadeTime, fadeDelay = .30, 0.15
-local saveButton
-
-local KeyButton_OnKeyDown = function(self, key)
-  local st = kb.saveTarget
-  kb.ProcessInput(key)
-end
-local KeyButton_OnKeyUp = function(self, key)
-  local st = kb.saveTarget
-end
-
-local KeyButton_OnClick = function(self, click)
-  print(self:GetName(), 'OnMouseDown', click)
-  local cursorType = GetCursorInfo()
-  if click == 'LeftButton' then
-    if cursorType then
-      kb.DropToSlot(self)
-    else
-      if self.command and self.isAvailable then
-        if IsShiftKeyDown() then
-          kb.db.stickyMode = true
-          KeyBinderStickyMode:SetChecked(true)
-        end
-        kb.ActivateSlot(self)
-        kb.ui()
-      end
-    end
-  elseif click == 'RightButton' then
-    kb.ReleaseSlot(self)
-    kb.ui()
-  else
-    kb.ProcessInput(strupper(click))
-  end
-end
-
-local KeyButton_OnDragStart = function(self)
-  kb.PickupSlot(self)
-end
-
-local KeyButton_OnReceiveDrag = function(self, ...)
-  kb.DropToSlot(self)
-end
-
-
-local KeyBinder_OnUpdate = function(self, elapsed)
-  self.elapsed = self.elapsed + elapsed
-  self.throttle = self.throttle + elapsed
-
-  if (self.throttle >= 0.032) then
-    self.throttle = 0
-  else
-    return
-  end
-
-  local progress = 1
-  if self.elapsed > fadeTime then
-    self.elapsed = 0
-    self.fadeStep = 0
-    --self.statustext:SetText(nil)
-    --self.bindingstext:SetText(nil)
-    self:SetScript('OnUpdate', nil)
-  else
-    if self.elapsed < fadeDelay then
-      progress = 0
-    else
-      self.fadeStep = self.fadeStep + 1
-      progress = (self.elapsed - fadeDelay) /(fadeTime - fadeDelay)
-    end
-    --print(self.fadeStep, format('%.02f/%.02f', (self.elapsed - fadeDelay) ,(fadeTime - fadeDelay)) , progress)
-  end
-
-  local alpha = 1 - progress * (1- restingAlpha)
-  self.statustext:SetAlpha(alpha)
-  self.bindingstext:SetAlpha(alpha)
-end
-
-local KeyButton_OnUpdate = function(self)
-  if not self.command then
-    return
-  end
-
-  if self:IsMouseOver() then
-    kb.elapsed = 0
-    if not self.active then
-      -- only set this handler when the button is activated/mouseOver
-      self.active = true
-      kb.statustext:SetText(self.statusText .. ': '..self.actionName)
-      kb.bindingstext:SetText(self.bindingText)
-      kb.fadeStep = 0
-      kb.throttle = 0
-      kb:SetScript('OnUpdate', KeyBinder_OnUpdate)
-
-      if kb.db.hoverInput and kb.saveTarget ~= self then
-        kb.ActivateSlot(self)
-        kb.ui()
-      end
-
-    end
-  else
-    if self.active and kb.db.hoverInput then
-      self.active = nil
-      --kb.DeactivateSlot(self)
-      --kb.ui()
-    end
-  end
-end
-
-local KeyBinder_OnMouseWheel = function(self, delta)
-  print(self, delta, self.scrollOffset, (self.scrollOffset <= 0))
-
-
-  if IsControlKeyDown() then
-    KEY_BUTTON_SIZE = KEY_BUTTON_SIZE - delta
-  else
-
-
-    if (delta > 0) and (self.scrollOffset <= 0) then
-      return
-    elseif delta < 0 and kb.scrollOffset >= 42 then
-      return
-    end
-    kb.scrollOffset = ceil(kb.scrollOffset - (delta * BINDS_PER_ROW))
-  end
-
-  for i = 1, #kb.buttons do
-      kb.buttons[i]:SetSize(KEY_BUTTON_SIZE,KEY_BUTTON_SIZE)
-  end
-
-  kb.ui(true)
-end
-
-local frameCount = 0
-local lastCheckFrame
-local KeyBinder_CheckButton = function(frame ,enableText, disableText, dbKey, tooltipText, callback, header)
-  if kb.db[dbKey] then
-    frame:SetChecked(true)
-  end
-
-  frame.header:SetText(header)
-
-  frame:SetScript('OnClick', function(self)
-    kb.db[dbKey] = self:GetChecked()
-    if callback then
-      callback(self)
-    end
-    kb.ui()
-  end)
-
-  frame:SetScript('OnEnter', function(self)
-    if tooltipText then
-      GameTooltip:SetOwner(self)
-      GameTooltip:SetText(tooltipText)
-      GameTooltip:Show()
-    end
-  end)
-
-  frame:SetScript('OnLeave', function(self)
-    if tooltipText and GameTooltip:GetOwner() == self then
-      GameTooltip:Hide()
-    end
-  end)
-
-  if frame:GetID() == 0 then
-    frameCount = frameCount + 1
-    frame:SetID(frameCount)
-    print('checkbutton #', frameCount)
-    if frameCount == 1 then
-      frame:ClearAllPoints()
-      frame:SetPoint('TOP', KeyBinderInventoryButton, 'BOTTOM', 0, -22)
-      frame:SetPoint('LEFT', kb.sourcesbg, 'LEFT', 2, 0)
-    else
-      frame:ClearAllPoints()
-      frame:SetPoint('TOPLEFT', lastCheckFrame, 'BOTTOMLEFT', 0, -2)
-    end
-
-    frame.header:ClearAllPoints()
-    frame.header:SetPoint('LEFT', frame, 'RIGHT', 2, 0)
-
-    lastCheckFrame = frame
-  end
-end
-
-local KeyBinder_OnHide = function()
-  KeyBinderImportLog:Hide()
-end
-
-local CloseButton_OnClick = function()
-  kb.db.showUI = false
-  kb:Hide()
-end
-local CancelButton_OnClick = function()
-  kb.RevertBindings()
-end
-local SaveButton_OnClick = function()
-  kb.ConfirmBindings()
-end
-
-
-local KeyBinder_Initialize = function()
-  do
-    local leftSlot, upSlot
-    for index = 1, NUM_KEY_SLOTS do
-
-      local button = CreateFrame('CheckButton', 'KeyBinderSlot'..index, kb, 'KeyButton')
-      button:SetScript('OnClick', KeyButton_OnClick)
-      button:SetScript('OnUpdate', KeyButton_OnUpdate)
-      button:SetScript('OnDragStart', KeyButton_OnDragStart)
-      button:SetScript('OnReceiveDrag', KeyButton_OnReceiveDrag)
-      button:RegisterForClicks('AnyUp')
-
-
-      local newRow = (mod(index, BINDS_PER_ROW) == 1)
-
-      if index == 1 then
-        button:SetPoint('TOPLEFT', kb.bg, 'TOPLEFT', BUTTON_PADDING, - BUTTON_PADDING)
-        upSlot = button
-      elseif newRow then
-        button:SetPoint('TOPLEFT', upSlot, 'BOTTOMLEFT', 0, -BUTTON_SPACING)
-        upSlot = button
-      else
-        button:SetPoint('TOPLEFT', leftSlot, 'TOPRIGHT', BUTTON_HSPACING, 0)
-      end
-
-      button:SetSize(KEY_BUTTON_SIZE, KEY_BUTTON_SIZE)
-      button:Show()
-      kb.buttons[index] = button
-      leftSlot = button
-    end
-  end
-
-
-  kb.scrollOffset = 0
-  kb.tabAnchor = {'TOPLEFT', kb.profilebg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING}
-  kb.tabGrowth = {'TOPLEFT', nil,'TOPRIGHT', BUTTON_SPACING, 0}
-  kb.tabSize = {TAB_HEIGHT, TAB_HEIGHT }
-  kb.UIPanelAnchor = {'TOPLEFT', kb.sourcesbg, 'TOPLEFT', BUTTON_PADDING, -BUTTON_SPACING}
-  kb.UIPanelGrowth = {'TOPLEFT', nil, 'BOTTOMLEFT', 0, -2 }
-  kb.UIPanelSize = {84, 32 }
-  kb.UIPanelIcon = {24, 32, 'LEFT', -12, 0}
-  kb.controlsAnchor = {'BOTTOMRIGHT', kb.footer, -BUTTON_PADDING, BUTTON_PADDING }
-  kb.controlsGrowth = {'BOTTOMRIGHT', nil, 'BOTTOMLEFT', -BUTTON_SPACING, 0}
-
-  -- order of these is important
-  kb:tab('KeyBinderGlobalTab',
-    kb.configTitle[BINDING_TYPE_GLOBAL] .. '\n' .. kb.configDescription[BINDING_TYPE_GLOBAL], "Interface\\ICONS\\item_azereansphere", {0.15,.85,.15,.85})
-  kb:tab('KeyBinderCharacterTab',
-    kb.configHeaders[BINDING_TYPE_CHARACTER] .. '\n' .. kb.configDescription[BINDING_TYPE_CHARACTER], nil)
-  kb:tab('KeyBinderSpecTab',
-    kb.configHeaders[BINDING_TYPE_SPECIALIZATION] .. '\n' .. kb.configDescription[BINDING_TYPE_SPECIALIZATION], kb.specInfo.texture)
-  KeyBinderCharacterTab.icon:SetTexCoord(0.15,.85,.15,.85)
-
-
-
-  --portraitLayers[1] = KeyBinderCharacterTab.icon
-  -- todo: find some generic icons for refresh/key input,etc
-
-  saveButton = kb:button('KeyBinderSaveButton', 'Update', 'Reload current bindings and refresh panel.', SaveButton_OnClick)
-  --restoreButton = kb:button('KeyBinderRestoreButton', 'Discard', 'Revert all changes.', CancelButton_OnClick)
-  --clearButton = kb:button('KeyBinderClearButton', 'Clear Page', 'Release all buttons.', ResetButton_OnClick)
-
-  kb:uibutton(
-    'KeyBinderSpellBookButton', 'SpellBook', nil,
-    function() ToggleSpellBook(BOOKTYPE_SPELL) end,
-    "Interface\\BUTTONS\\UI-MicroButton-Spellbook-Up", {0, 1, .4, 1})
-  kb:uibutton(
-    'KeyBinderTalentFrameButton', TALENTS, SPECIALIZATION,
-    function() ToggleTalentFrame() end,
-    "Interface\\BUTTONS\\UI-MicroButton-Talents-Up", {0, 1, .4, 1})
-
-  kb:uibutton(
-    'KeyBinderMacroFrameButton', 'Macros', nil,
-    function() if MacroFrame and MacroFrame:IsVisible() then
-      HideUIPanel(MacroFrame)
-    else
-      ShowMacroFrame() end
-    end,
-    "Interface\\BUTTONS\\UI-MicroButton-Help-Up", {0, 1, .4, 1})
-
-  kb:uibutton(
-    'KeyBinderInventoryButton', 'Bags', nil,
-    function() OpenAllBags() end,
-    "Interface\\BUTTONS\\UI-MicroButtonCharacter-Up", {0, 1, .4, 1})
-
-  KeyBinder_CheckButton(KeyBinderStickyMode, 'Enabled', 'Disabled', 'stickyMode', 'Keep input active after receiving a key.', function()
-    if kb.saveTarget and (not kb.db.stickyMode) then
-      kb.DeactivateSlot(kb.saveTarget)
-    end
-  end, 'Sticky:')
-  KeyBinder_CheckButton(KeyBinderHoverInput, 'MouseOver', 'Click', 'hoverInput', 'Enable key input when the cursor is over a binding slot.', nil, 'Bind by:')
-  KeyBinder_CheckButton(KeyBinderProtectBindings, 'Block', 'Allow', 'protectBlizKeys', 'Allow overwriting Blizzard UI bindings.', nil, 'Safety:')
-
-
-  KeyBinderUnbindButton:SetScript('OnClick', function()
-    if kb.saveTarget then
-      kb.UnbindSlot(kb.saveTarget)
-    end
-    kb.ui()
-  end)
-
-
-  kb.info:SetPoint('TOPLEFT', kb.UIPanels[1], 'BOTTOMLEFT', 0, -BUTTON_SPACING)
-  HEADER_OFFSET = kb.UIPanels[1]:GetHeight() + BUTTON_PADDING
-      + kb.info:GetHeight()
-  FOOTER_OFFSET = saveButton:GetHeight() + BUTTON_PADDING
-
-  kb:SetScript('OnHide', KeyBinder_OnHide)
-  kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel)
-  kb.CloseButton:SetScript('OnClick', CloseButton_OnClick)
-
-end
-
-
---- Retrieves button at index; creates said button and instates any stored parameters
-
-
-kb.ActivateSlot = function(button)
-  kb.saveTarget = button
-  kb:SetScript('OnKeyUp', function(self, key) KeyButton_OnKeyUp(button, key) end)
-  kb:SetScript('OnKeyDown', function(self, key) KeyButton_OnKeyDown(button, key) end)
-  kb:SetScript('OnMouseUp', function(self, key) KeyButton_OnKeyUp(button, key) end)
-  kb.savingText:ClearAllPoints()
-  kb.savingText:SetParent(button)
-  kb.savingText:SetPoint('BOTTOMLEFT', button, 'TOPLEFT', 0, 0)
-end
-
-kb.DeactivateSlot = function(button)
-  kb.saveTarget = nil
-  kb:SetScript('OnKeyUp', nil)
-  kb:SetScript('OnKeyDown', nil)
-  kb:SetScript('OnMouseUp', nil)
-end
-
-
---- push current information into living UI
-kb.ui = function(force)
-  for i, module in ipairs(kb.modules) do
-    if module.ui then
-      module.ui(force)
-    end
-  end
-
-  if InCombatLockdown() or not kb.db.showUI then
-    print('---end of refresh')
-    return
-  end
-  if not kb.loaded then
-    KeyBinder_Initialize()
-    kb.loaded = true
-  end
-  for i, button in ipairs(kb.buttons) do
-    button:SetID(i+kb.scrollOffset)
-    kb.UpdateSlot(button, force)
-  end
-
-
-  --- Frame Sizing
-  kb.profilebg:SetHeight(kb.tabSize[2] + BUTTON_PADDING * 2 + kb.profiletext:GetStringHeight())
-
-  kb.bg:SetWidth((KEY_BUTTON_SIZE + BUTTON_HSPACING + BUTTON_SPACING) * BINDS_PER_ROW + BUTTON_PADDING*2 - BUTTON_SPACING)
-  local numRows = NUM_KEY_SLOTS/BINDS_PER_ROW
-
-  kb.bg:SetHeight((KEY_BUTTON_SIZE + BUTTON_SPACING) * numRows + BUTTON_PADDING*2 - BUTTON_SPACING)
-
-  kb:SetHeight(kb.headerbg:GetHeight() + kb.profilebg:GetHeight() + kb.bg:GetHeight() + kb.footer:GetHeight())
-  kb:SetWidth((kb.sourcesbg:GetWidth() +(BINDS_PER_ROW * (KEY_BUTTON_SIZE + BUTTON_HSPACING) + (BINDS_PER_ROW - 1) * BUTTON_SPACING + BUTTON_PADDING * 2) ))
-
-  if kb.saveTarget then
-    kb.bg:SetColorTexture(.2,.5, .2, .5)
-    kb.savingText:Show()
-
-  else
-    kb.bg:SetColorTexture(unpack(BINDING_SCHEME_COLOR[kb.db.bindMode]))
-    kb.savingText:Hide()
-  end
-
-  for i, tab in ipairs(kb.tabButtons) do
-    local border = tab:GetNormalTexture()
-    local tabTexture = "Interface\\Buttons\\UI-Quickslot2"
-    local left, top, right, bottom = -12, 12, 13, -13
-    if i == kb.db.bindMode then
-      tabTexture = "Interface\\Buttons\\CheckButtonGlow"
-      left, top, right, bottom = -14, 14, 15, -15
-      tab.icon:SetDesaturated(false)
-      if tab.icon2 then tab.icon2:SetDesaturated(false) end
-      border:SetDesaturated(true)
-      border:SetVertexColor(1,1,1, 1)
-    else
-      tab.icon:SetDesaturated(true)
-      if tab.icon2 then tab.icon2:SetDesaturated(true) end
-      border:SetDesaturated(false)
-      border:SetVertexColor(1,1,1)
-    end
-    border:SetTexture(tabTexture)
-    border:SetPoint('TOPLEFT', tab, 'TOPLEFT', left, top)
-    border:SetPoint('BOTTOMRIGHT', tab, 'BOTTOMRIGHT', right, bottom)
-  end
-
-  KeyBinderSpecTab.icon:SetTexture(kb.specInfo.texture)
-  SetPortraitTexture(KeyBinderCharacterTab.icon, 'player')
-
-  kb.profiletext:SetText(kb.configHeaders[kb.db.bindMode])
-  print(kb.db.bindMode, kb.configHeaders[kb.db.bindMode], kb:GetSize())
-  print(kb:GetPoint(1))
-
-  kb:Show()
-
-  if kb.saveTarget then
-    KeyBinderUnbindButton:SetParent(kb.saveTarget)
-    KeyBinderUnbindButton:SetPoint('TOPLEFT', kb.saveTarget, 'BOTTOMLEFT', 0, -1)
-    KeyBinderUnbindButton:Show()
-  else
-    KeyBinderUnbindButton:Hide()
-  end
-
-  -- Reset this so talent cache can be rebuilt
-  kb.talentsPushed = nil
-end
-
---- Associate processed input with the given slot's metadata
-kb.SaveSlot = function(self, key)
-
-  if not self.command then
-    return
-  end
-  if InCombatLockdown() then
-    kb:print(L('Bindings cannot be changed during combat.'))
-    return
-  end
-
-
-  print('|cFFFFFF00received|cFFFFFF00', self:GetID(), '|cFF00FFFF', key)
-
-  local modifier = ''
-  if IsAltKeyDown() then
-    modifier = 'ALT-'
-  end
-  if IsControlKeyDown() then
-    modifier = modifier.. 'CTRL-'
-  end
-  if IsShiftKeyDown() then
-    modifier = modifier..'SHIFT-'
-  end
-  local binding = modifier..key
-
-  -- check for system bindings
-  --bprint('|cFFFFFF00SaveBind|r', 'protectKeys', kb.db.protectBlizKeys)
-  if kb.db.protectBlizKeys and kb.SystemBindings[binding] then
-    kb.statustext:SetText(L('BINDING_FAILED_PROTECTED', binding, kb.SystemBindings[binding]))
-    kb.bindingstext:SetText(nil)
-    return false
-  end
-
-  -- check for other keys
-  local previousCommand = GetBindingAction(binding)
-  if previousCommand ~= "" and previousCommand ~= self.command then
-    local actionType, actionID, name = kb.GetCommandAction(previousCommand)
-    if actionType then
-      local keys = {GetBindingKey(previousCommand) }
-      local  i = 1
-      while keys[i] do
-        if keys[i] == binding then
-          tremove(keys, i)
-          kb.UpdateBindingsCache(actionType, actionID, keys)
-          break
-        end
-        i = i + 1
-      end
-    end
-  end
-
-  local currentHotKeys = {GetBindingKey(self.command)}
-  local found
-  for i, key in ipairs(currentHotKeys) do
-    if key == binding then
-      found = true
-      print('|cFFFF4400key already bound to this')
-      return true
-    end
-  end
-  if not found then
-    tinsert(currentHotKeys, 1, binding)
-    kb.UpdateBindingsCache(self.actionType, self.actionID, currentHotKeys)
-  end
-
-  -- scour profile data for any conflicting binds
-  local currentAction = GetBindingAction(binding)
-  if match(currentAction, 'KeyBinder') then
-    if currentAction ~= self.command then
-      print('|cFFFF4400removing bindings for:', currentAction)
-      for profileID, profileData in ipairs(kb.loadedProfiles) do
-        local buttonID = profileData.commands[currentAction]
-        if buttonID then
-          local buttonAction = profileData.buttons[buttonID][2]
-          if buttonAction then
-            local talentInfo = profileData.talents[buttonAction]
-            if talentInfo and GetSpellInfo(buttonAction) then
-              for i = #talentInfo, 5, -1  do
-                if binding == talentInfo[i] then
-                  tremove(talentInfo, i)
-                end
-              end
-              kb:print(L('Overwrote talent |cFF88FF00%s|r in |cFF00FFFF%s|r', buttonAction, kb.configHeaders[profileID]))
-            else
-
-              kb:print(L('Overwrote |cFFFFFF00%s|r in |cFF00FFFF%s|r', buttonAction, kb.configHeaders[profileID]))
-            end
-            profileData.bindings[binding] = nil
-          end
-        end
-      end
-    end
-  end
-
-
-  print('SetBinding', binding, self.command)
-  SetBinding(binding, self.command)
-  SaveBindings(GetCurrentBindingSet())
-  self.binding = binding
-
-  local talentName = self.actionName
-  if self.actionType == 'macro' then
-    talentName = GetMacroSpell(self.actionID)
-  end
-
-  local talentInfo
-  if talentName and kb.TalentCache[talentName] then
-    print('store dynamicType talent')
-    talentInfo = {self.macroName, self.actionName, self.actionType, self.actionID}
-    local bindings = {GetBindingKey(self.command) }
-    for i, key in ipairs(bindings) do
-      tinsert(talentInfo, key)
-    end
-  end
-
-  for level, profile in ipairs(kb.orderedProfiles) do
-    if (level == kb.db.bindMode) then
-      profile.bound[self.command] = true
-      if talentInfo then
-        profile.bindings[self.binding] = nil
-      else
-        profile.bindings[self.binding] = self.command
-      end
-      if talentName then
-        profile.talents[talentName] = talentInfo
-      end
-    else
-      profile.bindings[self.binding] = nil
-      profile.bound[self.command] = nil
-      if talentName then
-        profile.talents[talentName] = nil
-      end
-    end
-  end
-
-
-  kb:print(L('BINDING_ASSIGNED', self.binding, self.actionName, kb.currentHeader))
-  kb.ui()
-  return true
-end
-
-
-kb.UnbindSlot = function(self)
-
-  local keys = {GetBindingKey(self.command) }
-  if #keys >= 1 then
-    kb.UpdateBindingsCache(self.actionType, self.actionID, {})
-  end
-
-  local talentName = self.actionName
-  if self.actionType == 'macro' then
-    local spellName, _, spellID = GetMacroSpell(self.actionID)
-    talentName = spellName
-  end
-
-
-  --print('detected', #keys, 'bindings')
-  for i, key in pairs(keys) do
-    --print('clearing', key)
-    SetBinding(key, nil)
-    SaveBindings(GetCurrentBindingSet())
-    if kb.currentProfile.bindings[key] then
-      --kb:print(L('BINDING_REMOVED', self.actionName, kb.currentHeader))
-      kb.currentProfile.bindings[key] = nil
-    end
-    if kb.currentProfile.talents[talentName] then
-      kb.currentProfile.talents[talentName] = nil
-    end
-
-    kb.bindings[tostring(self.actionType)..'_'..tostring(self.actionID)] = nil
-  end
-  if kb.currentProfile.bound[self.command] then
-    kb.currentProfile.bound[self.command] = nil
-    --kb:print(BINDING_REMOVED:format(self.actionName, configHeaders[db.bindMode]))
-  end
-
-
-  self.active = false
-  kb.UpdateSlot(self, true)
-end
-
-kb.AcceptAssignment = function(self, ...)
-  local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
-  local source = kb.  loadedProfiles[popup.oldProfile]
-  kb.SetSlot(popup.slot, unpack(popup.args))
-  kb.UpdateSlot(popup.slot)
-  kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) -- re-enable scrolling
-  ClearCursor()
-  ResetCursor()
-end
-
---- Add to blizzard interfaces
-StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"] = {
-  text = "Confirm moving an assigned command.",
-  button1 = OKAY,
-  button2 = CANCEL,
-  timeout = 0,
-  whileDead = 1,
-  showAlert = 1,
-  OnAccept = kb.AcceptAssignment,
-  OnCancel = function() kb:SetScript('OnMouseWheel', KeyBinder_OnMouseWheel) end
-}
\ No newline at end of file
--- a/SkeletonKey/Events.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +0,0 @@
--- KrakTool
--- Events.lua
--- Created: 7/24/2016 11:10 PM
--- %file-revision%
--- Event handlers, and the init block that sets them up; nothing else should be here
-
-local kb, print = LibStub("LibKraken").register(KeyBinder)
-
-kb.init = function()
-  kb:RegisterEvent('PLAYER_ENTERING_WORLD')
-  kb:RegisterEvent('UPDATE_MACROS')
-  kb:RegisterEvent('UPDATE_BINDINGS')
-  kb:RegisterUnitEvent('PLAYER_SPECIALIZATION_CHANGED', 'player', 'pet')
-  kb:RegisterUnitEvent('UNIT_PORTRAIT_UPDATE', 'player', 'pet')
-  kb:RegisterUnitEvent('SPELLS_CHANGED')
-  kb:RegisterUnitEvent('TALENT_UPDATE', 'player', 'pet')
-
-  kb:RegisterEvent('PLAYER_REGEN_DISABLED')
-  kb:RegisterEvent('PLAYER_REGEN_ENABLED')
-end
-
-kb.PLAYER_REGEN_DISABLED = function()
-  kb:Hide()
-end
-
-kb.UNIT_PORTRAIT_UPDATE = function()
-  if KeyBinderCharacterTab then
-    SetPortraitTexture(KeyBinderCharacterTab.icon, 'player')
-  end
-end
-
-kb.PLAYER_REGEN_ENABLED = function()
-  kb.ui()
-end
-
-kb.PLAYER_SPECIALIZATION_CHANGED =  function(...)
-  kb.UpdateSpecInfo()
-  kb.UpdateTalentInfo()
-  kb.SelectProfileSet(kb.profileName)
-  kb.ApplyAllBindings()
-  kb.ui(true)
-end
-kb.PLAYER_TALENT_UPDATE = function()
-  kb.UpdateTalentInfo()
-  kb.SelectProfileSet(kb.profileName)
-  kb.ApplyAllBindings()
-  kb.ui()
-end
-kb.ACTIONBAR_SLOT_CHANGED = function(self, event, slot)
-  --kb.HotKeyText(slot)
-  return true
-end
-
--- only need to respond to this for pet actions
-kb.SPELLS_CHANGED = function(self, event, unit)
-  print('|cFFFF0088'.. event..'|r', unit)
-  kb.UpdatePetInfo()
-end
-
-kb.UPDATE_MACROS = function()
-  kb.UpdateMacroInfo()
-end
-
-kb.UPDATE_BINDINGS = function()
-  kb.UpdateSystemBinds()
-end
\ No newline at end of file
--- a/SkeletonKey/HotKey.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,271 +0,0 @@
--- KrakTool
--- HotKey.lua
--- Created: 7/22/2016 10:28 PM
--- %file-revision%
--- Module for fixing actionbar hotkey text
-
-local _G, wipe, tContains, tinsert = _G, table.wipe, tContains, tinsert
-
-local hotkey = CreateFrame('Frame', 'SkeletonHotKeys', UIParent)
-local kb, print, wrap = LibStub("LibKraken").register(KeyBinder, hotkey)
-local hotkeyText = {}
-local blizHotKey = {}
-local bindings
-
--- frames obtained via post-load hooks, created by addons like Dominos or BarTender4
-local loadedFrames = {}
--- frames divided by update categories
-local categoryFrames = {}
--- frames indexed by action slot ID (just the action bar, for... reasons)
-local actionSlots = {}
-local actionFrames = {}
-local actionIndex = {}
-
-
---- Used to determine which groups of action buttons need updating
-local hotkeyEvents = {
-  ["UPDATE_BONUS_ACTIONBAR"] = {"bonus"},
-  ["UPDATE_VEHICLE_ACTIONBAR"] = {"vehicle"},
-  ["UPDATE_OVERRIDE_ACTIONBAR"] = {"override"},
-  ["ACTIONBAR_PAGE_CHANGED"] = {"actionbar"},
-  ["ACTIONBAR_SLOT_CHANGED"] = {"actionslot"},
-  ["PLAYER_ENTERING_WORLD"] = {"world","all"},
-  ["PET_UI_UPDATE"] = {"pet"},
-  ["PLAYER_SPECIALIZATION_CHANGED"] = {"player"},
-  ["PLAYER_TALENTS_UPDATED"] = {"player"},
-}
-
-
-hotkey.wrapEvent = function(event, ...)
-  hotkey:RegisterEvent(event)
-  hotkeyEvents[event] = {...}
-  hotkey[event] = hotkey.UpdateFromEvent
-end
-
-hotkey.unwrapEvent = function(event)
-  if not kb[event] then
-    kb:UnregisterEvent(event)
-  end
-  hotkeyEvents[event] = nil
-  hotkey[event] = nil
-end
-
-
-hotkey.RegisterFrame = function(frame)
-  --wrap(function()
-  print('ActionBarButtonEventsFrame_RegisterFrame(', frame:GetName(), frame.action, frame:IsVisible(), frame:IsShown())
-  --end)
-  blizHotKey[frame] = frame.HotKey
-  loadedFrames[frame] = true
-end
-
-hotkey.UpdateFromEvent = function(self, event, ...)
-  if hotkeyEvents[event] then
-    --print('call batch', event, ...)
-    for i, func  in ipairs(hotkeyEvents[event]) do
-
-      if hotkey[func] then
-        print('->|cFF88FF00', func)
-        hotkey[func](self, event, ...)
-      end
-    end
-  end
-  return true
-end
-
-hotkey.variables = function()
-  print('variables')
-  bindings = kb.GetBindings()
-  for event, manifest in pairs(hotkeyEvents) do
-    print('-', event, table.concat(manifest, ', '))
-    hotkey:RegisterEvent(event)
-    hotkey[event] = hotkey.UpdateFromEvent
-  end
-  hotkey.wrapEvent('UNIT_PET', 'pet')
-  hotkey.player()
-end
-
-
-hotkey.init = function()
-
-  hooksecurefunc("ActionBarButtonEventsFrame_RegisterFrame", hotkey.RegisterFrame)
-
-end
-
-hotkey.ui = function()
-  hotkey.player()
-  hotkey.pet()
-end
-
-
-hotkey.world = function()
-  -- needs to be delayed so it isn't fired 50 times at login
-  hotkeyEvents["UPDATE_BINDINGS"] = {"binding"}
-  hotkey.UPDATE_BINDINGS = hotkey.UpdateFromEvent
-  hotkey:RegisterEvent("UPDATE_BINDINGS")
-
-  hotkey.player()
-  hotkey.pet()
-end
-
--- requires all these arguments since non-actionbar buttons don't have all of said methods
-local kprint = (DEVIAN_WORKSPACE and function(...) _G.print('HotKeyUpdate', ...) end) or function() end
-hotkey.UpdateHotKey = function(frame, actionType, actionID, hasAction)
-  bindings = kb.GetBindings()
-
-  if hasAction then
-    local indexKey = kb.FormatActionID(actionType, actionID)
-    kprint(frame:GetName(), '|cFF88FF00'..indexKey..'|r',  hasAction)
-
-
-    local actionName
-    if actionType == 'spell' then
-      actionName = GetSpellInfo(actionID)
-    elseif actionType == 'macro' then
-      actionName = GetMacroInfo(actionID)
-    elseif actionType == 'summonmount' then
-      actionName = C_MountJournal.GetMountInfoByID(actionID)
-    elseif actionType == 'item' then
-      actionName = GetItemInfo(actionID)
-    end
-    kprint(' ', actionName)
-
-    local binds = bindings[indexKey]
-    kprint(binds)
-    if binds and (not frame.HotKey:IsVisible()) then
-      local bindingsText = kb.BindingString(unpack(binds))
-
-      if not hotkeyText[frame] then
-        kprint('-new hotkey element')
-        hotkeyText[frame] = frame:CreateFontString(frame:GetName()..'SkeletonKey', 'OVERLAY')
-        hotkeyText[frame]:SetFont(frame.HotKey:GetFont())
-        hotkeyText[frame]:SetTextColor(frame.HotKey:GetTextColor())
-        hotkeyText[frame]:SetPoint('TOPRIGHT', frame.HotKey, 'TOPRIGHT')
-
-        hooksecurefunc(frame.HotKey, 'SetVertexColor', function(self, r,g,b,a)
-          hotkeyText[frame]:SetTextColor(r,g,b,a)
-        end)
-      end
-
-      hotkeyText[frame]:SetText(bindingsText)
-      hotkeyText[frame]:Show()
-      kprint('   |cFF00FFFF', frame:GetName(), '|cFFFFFF00'..tostring(bindingsText)..'|r')
-
-      return
-    end
-  end
-
-  if hotkeyText[frame] then
-    local oldText = hotkeyText[frame]:GetText()
-    if oldText then
-    hotkeyText[frame]:SetText(nil)
-    print('|cFFFF4400' .. frame:GetName() .. '|r', 'removed text', oldText)
-    end
-
-  end
-end
-
-hotkey.actionbar = function()
-  -- reset frames list
-  print('actionbar')
-  wipe(actionFrames)
-  if ActionBarButtonEventsFrame.frames then
-    for index, frame in ipairs(ActionBarButtonEventsFrame.frames) do
-      local slot = frame.action
-      local actionType, actionID = GetActionInfo(slot)
-      local hasAction = HasAction(slot)
-
-      actionSlots[slot] = frame
-      if hasAction then
-        local indexKey = kb.FormatActionID(actionType, actionID)
-
-        actionFrames[indexKey] = actionFrames[indexKey] or {}
-        local result = ''
-        if not tContains(actionFrames[indexKey], frame) then
-          tinsert(actionFrames[indexKey], frame)
-          actionIndex[slot] = indexKey
-          result = 'added'
-        end
-        print('#'..index, frame:GetName(), indexKey, result)
-      end
-
-
-      hotkey.UpdateHotKey(frame, actionType, actionID, hasAction)
-    end
-  end
-end
-
-hotkey.actionslot = function(self, event, slot)
-  print(actionSlots[slot], event, slot, GetActionInfo(slot))
-  local atype, aid = GetActionInfo(slot)
-  local indexKey = kb.FormatActionID(atype, aid)
-  local frame = actionSlots[slot]
-  actionFrames[indexKey] = actionFrames[indexKey] or {}
-  if not tContains(actionFrames[indexKey]) then
-    tinsert(actionFrames[indexKey], frame)
-    actionIndex[slot] = indexKey
-  end
-  if frame then
-    hotkey.UpdateHotKey(frame, atype, aid, HasAction(slot))
-  end
-
-end
-
-hotkey.player = function()
-  print('player')
-  hotkey.actionbar()
-end
-
-
-
-hotkey.pet = function(self, event, arg1)
-  if event == 'UNIT_PET' and arg1 == 'player' then
-    if PetHasActionBar() and UnitIsVisible("pet") then
-      hotkey.wrapEvent('PET_UI_CLOSE', 'pet')
-      hotkey.wrapEvent('PET_BAR_UPDATE', 'pet')
-    else
-      hotkey.unwrapEvent('PET_UI_CLOSE')
-      hotkey.unwrapEvent('PET_BAR_UPDATE')
-      return
-    end
-  end
-
-  for i=1, NUM_PET_ACTION_SLOTS, 1 do
-    local button = _G['PetActionButton'.. i]
-    --print(button:GetName())
-    for k, v in pairs(button) do
-      --print(' ', k, type(v))
-    end
-  end
-end
-
-
---- used to pick up changes from user interaction
-local changeIndex = 1
-hotkey.binding = function()
-  print('|cFFFF0088BindingsUpdate')
-  local changeNum = #kb.ChangedBindings
-  if changeNum >= changeIndex then
-
-
-    for i = changeIndex, changeNum do
-      local actionType, actionID, name = unpack(kb.ChangedBindings[i])
-      local actionKey = kb.FormatActionID(actionType, actionID)
-      local frames = actionFrames[actionKey]
-      if frames then
-        for i, frame in ipairs(frames) do
-          print('|cFFFF0088'..actionKey..'|r', frame)
-          if frame then
-            hotkey.UpdateHotKey(frame , actionType, actionID, HasAction(frame.action))
-          end
-        end
-      else
-        print('no frames picked up, rebuild')
-        hotkey.actionbar()
-      end
-
-      changeIndex = i + 1
-    end
-
-  end
-end
\ No newline at end of file
--- a/SkeletonKey/Import.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,209 +0,0 @@
--- KrakTool
--- Import.lua
--- Created: 7/10/2016 6:20 AM
--- %file-revision%
--- Tools for first-time setup and migration from other addons.
-
-local kb = LibStub("LibKraken").register(KeyBinder, 'Import')
-local print = DEVIAN_WORKSPACE and  function(...) print('kbi', ...) end or function() end
-
-
-local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
-local SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME = "Summon Random Favorite Mount"
-local results = {}
-local importSet = setmetatable({}, {__tostring = function() return 'Global Bindings' end})
-kb.importTypes = {}
-
-local lineNumber = 0
-local usedBinds, usedKeys = {}, {}
-local profileName = ''
-local specProfile
-local msg = function(...) return KeyBinderImportLog:AddMessage(...) end
-
---- Core interfaces
-kb.ImportScan = function()
-  -- hang onto this
-  SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
-
-  local hasAnyData = false
-  for addon, module in pairs(kb.importTypes) do
-    if IsAddOnLoaded(addon) then
-      local hasData = module.GetChanges(importSet)
-      if hasData then
-        kb:print("|cFFFFFF00"..addon.." is currently enabled.")
-      end
-
-      hasAnyData = (hasData or hasAnyData)
-    end
-  end
-  if hasAnyData then
-    kb:print("You can use /skb import |cFF00FF00<AddOn Name>|r to attempt to copy settings related to this character. This will also disable that AddOn.")
-  end
-
-
-  kb.ui()
-  --KeyBinderImportLog:Show()
-
-  return importSet
-end
-
-kb.ImportCommmit = function(self, text)
-  kb.db.buttons = importSet.buttons
-  kb.db.bound = importSet.bound
-  kb.db.commands = importSet.commands
-  kb.db.bindings = importSet.bindings
-  kb.db.macros = importSet.macros
-  kb.db[profileName] = importSet[profileName]
-  kb.profile(profileName)
-
-
-  kb:ApplyAllBindings()
-  kb.ui()
-  kb:print('Imported settings applied! Note that you will need to change active specializations and run the command again to create their respective Char+Spec profiles.')
-end
-
---- Key Scan
-kb.importTypes.KeyBinder = {}
-kb.importTypes.BindPad = {}
-local kbi = kb.importTypes.KeyBinder
-local bp = kb.importTypes.BindPad
-
---- BindPad import process
--- because bindpad doesn't store class info, it is not possible to discern the identity of primary/secondary
-
-local GetBindPadSlot = function(profile, slot, data)
-  local actionType = data.type:lower()
-  local command = data.action:gsub('CLICK BindPadKey:%s*', '')
-  local clickAction = command:match('BindPadMacro:%s*(.+)%s*$')
-  local macroName, macroText
-  if clickAction then
-    local clickType = 'spell'
-    if clickAction == SUMMON_RANDOM_FAVORITE_MOUNT_SPELLNAME then
-      clickType = 'mount'
-      clickAction = 0
-    else
-      clickType = 'spell'
-    end
-
-    macroName, macroText, command = kb.RegisterAction(nil, clickType, clickAction)
-    profile.macros[macroName] = {macroText, command}
-  end
-  print('[|cFF00FF00'.. data.type .. '|r] |cFFFF00FF' .. data.action .. '|r :: "' .. tostring(clickAction) .. '"')
-
-  results[actionType] = (results[actionType] or 0) + 1
-  profile.buttons[slot] = {
-    command,
-    command:gsub(data.type, ''),
-    data.texture
-  }
-  profile.commands[command] = slot
-
-  if usedBinds[data.action] then
-    local key1, key2 = unpack(usedBinds[data.action])
-    print('adding keybind |cFF00FFFF' .. command .. ' |cFF00FF00' .. (key1) .. ' |cFF00FF00' .. (key2 or ''))
-    profile.bindings[key1] = command
-
-    if key2 then
-      profile.bindings[key2] = command
-    end
-
-    usedBinds[data.action] = nil
-
-    msg('|cFF00FFFF'..tostring(profile)..'|r '..slot..': ' .. '|cFF00FFFF' .. command .. '|r |cFF00FF00' .. (key1 or '') .. (key2 and ('|r, |cFF00FF00key2') or '') )
-  else
-    print('|cFFFF4400no binding|r ' .. command)
-  end
-end
-
-local GetBindPadProfile = function(profile, bp)
-  local bpnames = {'CharacterSpecificTab1', 'CharacterSpecificTab2', 'CharacterSpecificTab3'} -- won't need this ever again, let it fall into gc
-
-  -- resolve spec ID
-  local specID = GetSpecialization()
-  local globalID = GetSpecializationInfo(GetSpecialization())
-  local activeProfile
-
-  -- setup string coercions for debugging
-  setmetatable(profile,{__tostring = function(t) return 'Character' end})
-  if GetNumSpecGroups() > 1 then
-    local activeGroup = GetActiveSpecGroup()
-    activeProfile = bp.profileForTalentGroup[activeGroup]
-    profile[specID] = kb.InitProfile(profile[specID] or {})
-    specProfile = profile[specID]
-    setmetatable(specProfile,{__tostring = function(t) return 'Spec #'..specID end})
-  end
-
-  print('-')
-
-  results.spell = 0
-  results.macro = 0
-  results.click = 0
-  results.item = 0
-
-  table.wipe(usedBinds) -- [action] = {key1, key2}
-  table.wipe(usedKeys)  -- [key]    = "action"
-  for id, bpProfile in ipairs(bp) do
-
-    local slots = 0
-
-    if bpProfile.AllKeyBindings then
-      for binding, command in pairs(bpProfile.AllKeyBindings) do
-        -- each binding value
-        if not usedKeys[binding] then
-          usedKeys[binding] = command
-        end
-
-        usedBinds[command] = usedBinds[command]  or {}
-        tinsert(usedBinds[command], binding)
-      end
-    end
-
-
-
-    for i, name in ipairs(bpnames) do
-      -- each tab
-      if bpProfile[name] then
-        for slot, data in pairs(bpProfile[name]) do
-          if type(data) == 'table' then
-            slots = slots + 1
-
-
-            local profile = (id == activeProfile) and specProfile or profile
-            lineNumber = lineNumber + 1
-            GetBindPadSlot(profile, slot, data)
-
-          else
-            --print(type(data), slot, data)
-          end
-        end
-      end
-    end
-  end
-end
-
-
-bp.GetChanges = function(importSet)
-  -- ensure that subtables are there
-  kb.InitProfile(importSet)
-  if BindPadVars then
-    local globalSlots = 0
-    for k,v in pairs(BindPadVars) do
-      --print(k, type(k))
-      if type(k) == 'string' then
-        local realm, name = k:match("^PROFILE_([%S]+)_(%S+)$")
-        if realm == kb.playerRealm and name == kb.playerName then
-          profileName = realm .. '_' .. name
-          msg('Found character profile: |cFFFFFF00'.. tostring(realm) .. '|r-|cFF00FFFF'..tostring(name)..'|r')
-          importSet[profileName] = kb.InitProfile({})
-          importSet[profileName].imported = true
-          GetBindPadProfile(importSet[profileName], v)
-        end
-      elseif type(k) == 'number' and type(v) == 'table' then
-        globalSlots = globalSlots + 1
-        GetBindPadSlot(importSet, globalSlots, v)
-      end
-    end
-  end
-
-  return results
-end
--- a/SkeletonKey/KeyButton.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,544 +0,0 @@
--- SkeletonKey
--- KeyButton.lua
--- Created: 7/28/2016 11:26 PM
--- %file-revision%
--- Deals with display and manipulation of binding slots
-
-local kb, print = LibStub('LibKraken').register(KeyBinder, 'Slot')
-local L = kb.L
-local type, tonumber, tostring, tinsert, tremove, ipairs, pairs = type, tonumber, tostring, tinsert, tremove, ipairs, pairs
-local _G, unpack, select, tostring = _G, unpack, select, tostring
-local GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo = GetSpellBookItemName, GetSpellBookItemTexture, GetSpellBookItemInfo, GetPetActionInfo
-local GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon = GetSpellInfo, GetMacroInfo, GetItemInfo, GetItemIcon
-local GetCursorInfo, ClearCursor, ResetCursor = GetCursorInfo, ClearCursor, ResetCursor
-local GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown, IsShiftKeyDown = GetSpellTexture, IsTalentSpell, GetMacroIndexByName, IsAltKeyDown, IsControlKeyDown,IsShiftKeyDown
-local GetBindingKey, GetProfessionInfo = GetBindingKey, GetProfessionInfo
-local GetMountInfoByID, GetPetInfoByPetID = C_MountJournal.GetMountInfoByID, C_PetJournal.GetPetInfoByPetID
-
-local CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_PETACTION, CURSOR_TEXTURE
-local SUMMON_RANDOM_FAVORITE_MOUNT_SPELL = 150544
-local BORDER_UNASSIGNED = {0.2,0.2,0.2,1}
-local BORDER_ASSIGNED = {1,1,1,1}
-local BORDER_DYNAMIC = {1,1,0,1}
-local BORDER_PENDING = {1,0.5,0,1 }
-local BUTTON_HEADERS = {
-  ['spell'] = SPELLS,
-  ['macro'] = MACRO,
-  ['petaction'] = PET,
-  ['mount'] = MOUNT,
-  ['battlepet'] = BATTLEPET,
-
-
-  [5] = PROFESSIONS_FIRST_AID,
-  [7] = PROFESSIONS_COOKING,
-  [9] = PROFESSIONS_FISHING,
-  [10] = PROFESSIONS_ARCHAEOLOGY,
-
-}
-
-local PROFESSION_HEADERS = {
-  [1] = 'Profession 1',
-  [2] = 'Profession 2',
-  [3] = 10,
-  [4] = 7,
-  [5] = 9,
-  [6] = 5
-}
-
--- This is needed to identify a spells that aren't reflected by GetCursorInfo()
-kb.OnPickupPetAction = function(slot, ...)
-  local isPickup = GetCursorInfo()
-  print(slot, ...)
-  if kb.PetCache.action[slot] then
-    if isPickup then
-      local key, _, texture = unpack(kb.PetCache.action[slot])
-      local spellName = _G[key] or key
-      if spellName and kb.PetCache.spellslot[spellName] then
-        CURSOR_SPELLSLOT = kb.PetCache.spellslot[spellName][1]
-        CURSOR_BOOKTYPE = BOOKTYPE_PET
-        CURSOR_TEXTURE = _G[texture] or texture
-      end
-    else
-      CURSOR_SPELLSLOT = nil
-      CURSOR_BOOKTYPE = nil
-      CURSOR_TEXTURE = nil
-    end
-    print('|cFFFF4400PickupPetAction|r', isPickup, CURSOR_PETACTION)
-  end
-
-  local name, subtext, texture, isToken = GetPetActionInfo(slot)
-  if name then
-    kb.PetCache.action[slot] = {name, subtext, texture, isToken}
-  end
-
-
-  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
-
-end
-
-kb.OnPickupSpellBookItem = function(slot, bookType)
-  print('|cFFFF4400PickupSpellBookItem('.. tostring(slot).. ', '..tostring(bookType)..')')
-  CURSOR_SPELLSLOT = slot
-  CURSOR_BOOKTYPE = bookType
-  CURSOR_TEXTURE = GetSpellBookItemTexture(slot, bookType)
-  print('current cursor info', CURSOR_SPELLSLOT, CURSOR_BOOKTYPE, CURSOR_TEXTURE)
-end
-
-kb.CreateHooks = function()
-  hooksecurefunc("PickupSpellBookItem", kb.OnPickupSpellBookItem)
-  hooksecurefunc("PickupPetAction", kb.OnPickupPetAction)
-end
-
-
-kb.DropToSlot = function(self)
-  print(self:GetName(),'|cFF0088FFreceived|r')
-  local actionType, actionID, subType, subData = GetCursorInfo()
-  print('GetCursorInfo', GetCursorInfo())
-  if actionType then
-
-    if actionType == 'flyout' then
-      ClearCursor()
-      ResetCursor()
-      return
-    end
-
-
-    local name, icon, _
-    local pickupID, pickupBook
-
-    if actionType == 'spell' then
-      actionID = subData
-      name, _, icon = GetSpellInfo(actionID)
-
-    elseif actionType == 'macro' then
-      name, icon = GetMacroInfo(actionID)
-    elseif actionType == 'petaction' then
-      if CURSOR_SPELLSLOT and CURSOR_BOOKTYPE then
-
-        local spellType, spellID = GetSpellBookItemInfo(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
-        local spellName, spellText = GetSpellBookItemName(CURSOR_SPELLSLOT, CURSOR_BOOKTYPE)
-        if spellType == 'PETACTION' then
-          name = spellName
-          actionID = spellText
-          icon = CURSOR_TEXTURE
-        else
-          name, _, icon = GetSpellInfo(spellID)
-          actionID = spellID
-        end
-
-        pickupID = CURSOR_SPELLSLOT
-        pickupBook = CURSOR_BOOKTYPE
-      else
-
-
-      end
-
-    elseif actionType == 'mount' then
-      if subType == 0 then
-        name, _, icon = GetSpellInfo(SUMMON_RANDOM_FAVORITE_MOUNT_SPELL)
-        actionID = 0
-      else
-        name, _, icon = GetMountInfoByID(actionID)
-      end
-    elseif actionType == 'item' then
-      name = GetItemInfo(actionID)
-      icon = GetItemIcon(actionID)
-    elseif actionType == 'battlepet' then
-
-      local speciesID, customName, level, xp, maxXp, displayID, isFavorite, petName, petIcon, petType, creatureID = GetPetInfoByPetID(actionID)
-      name = customName or petName
-      icon = petIcon
-
-    end
-    local macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)
-
-
-    local isAssigned, isBound, assignedBy, boundBy = kb.IsCommandBound(self, command)
-    if isAssigned then
-      local popup = StaticPopupDialogs["SKELETONKEY_CONFIRM_ASSIGN_SLOT"]
-      popup.slot = self
-      popup.text = "Currently assigned in |cFFFFFF00"..tostring(kb.configHeaders[assignedBy]).."|r. Are you sure?"
-      popup.oldProfile = assignedBy
-      popup.args = {command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook }
-      kb:SetScript('OnMouseWheel', nil) -- disable scrolling
-      StaticPopup_Show('SKELETONKEY_CONFIRM_ASSIGN_SLOT')
-    else
-      kb.SetSlot(self, command, name, icon, actionType, actionID, macroName, macroText, pickupID, pickupBook)
-      kb.UpdateSlot(self)
-      self.active = nil
-      ClearCursor()
-      ResetCursor()
-    end
-  end
-end
-
-
-do
-  local PickupAction = {
-    spell = _G.PickupSpell,
-    petaction =
-      function(...)
-        -- needs to be enclosed to acquire hooksecurefunc effects
-        _G.PickupSpellBookItem(...)
-      end,
-    macro = _G.PickupMacro,
-    item = _G.PickupItem,
-    mount = _G.C_MountJournal.Pickup
-  }
-  local GetPickupValue = {
-    spell = function(self) return select(7, GetSpellInfo(self.actionID)) end,
-    petaction = function(self) return self.pickupSlot, self.pickupBook end,
-  }
-  kb.PickupSlot = function(self)
-    if not (self.command and self.isAvailable) then
-      return
-    end
-    print(self.actionType)
-    if self.actionType == 'spell' then
-      -- It can't be picked up if SpellInfo(name) returns void
-      local dummy = GetSpellInfo(self.actionName)
-      if not dummy then
-        return
-      end
-    end
-    if PickupAction[self.actionType] then
-      if GetPickupValue[self.actionType] then
-        PickupAction[self.actionType](GetPickupValue[self.actionType](self))
-      else
-        PickupAction[self.actionType](self.actionID)
-      end
-      kb.ReleaseSlot(self)
-      kb.UpdateSlot(self)
-    end
-  end
-end
-
-
-
-
---- Updates profile assignment and button contents
-kb.UpdateSlot = function(self, force)
-  local slot = self:GetID()
-
-  if force then
-    if kb.currentProfile.buttons[slot] then
-      kb.SetSlot(self, unpack(kb.currentProfile.buttons[slot]))
-    else
-      kb.ReleaseSlot(self)
-    end
-  end
-
-
-  if self.command then
-    print('['..slot..'] =', self.command, GetBindingKey(self.command))
-
-    if not self.isAvailable then
-      self.border:SetColorTexture(1,0,0,1)
-      self.ignoreTexture:Show()
-    else
-      self.ignoreTexture:Hide()
-
-      if self.pending then
-        self.border:SetColorTexture(unpack(BORDER_PENDING))
-      elseif self.isDynamic then
-        self.border:SetColorTexture(unpack(BORDER_DYNAMIC))
-      else
-        self.border:SetColorTexture(unpack(BORDER_ASSIGNED))
-      end
-    end
-
-
-    if self.actionType == 'macro' then
-      self.macro:Show()
-    else
-      self.macro:Hide()
-      if self.actionType == 'spell' then
-        local dummy = GetSpellInfo(self.actionName)
-        if not dummy then
-          self.icon:SetDesaturated(true)
-        else
-          self.icon:SetDesaturated(false)
-        end
-
-      end
-    end
-
-    if self.isDynamic then
-      print('|cFF00BBFFUpdateSlot|r:', self.isDynamic, self.isAvailable, self.actionID)
-    end
-
-    if self.isDynamic == 'profession'  then
-      if self.profIndex then
-
-        local profText = (self.spellNum == 1) and TRADE_SKILLS or (BUTTON_HEADERS[self.profIndex] or GetProfessionInfo(self.profIndex))
-        print(self.profIndex, 'spnum', type(self.spellNum), (self.spellNum == 1))
-
-        self.statusText = '|cFFFFFF00'..tostring(profText)..'|r'
-        self.bindingText = kb.BindingString(GetBindingKey(self.command))
-      else
-
-        self.statusText = '|cFFFF4400'..PROFESSION_HEADERS[self.professionNum]..'|r'
-        self.actionName = '(#'..self.professionNum..')'
-        self.bindingText ='?'
-      end
-    elseif self.isDynamic == 'talent' then
-
-      self.statusText = '|cFF00FFFF'.. TALENT .. '|r'
-      if self.isAvailable then
-        self.bindingText = kb.BindingString(GetBindingKey(self.command))
-      else
-        if kb.TalentBindings[self.actionID] then
-          print(self.actionID, #kb.TalentBindings[self.actionID])
-          self.bindingText= kb.BindingString(unpack(kb.TalentBindings[self.actionID]))
-        end
-
-      end
-    elseif self.isDynamic == 'petaction' then
-      local specialType, specialNum =  self.command:match("petaction_([%a%s]+)_(%d)")
-      if specialType and specialNum then
-        print('pet skill|cFF00FF00', specialType..'|r', specialNum)
-        self.statusText = L(specialType..' %%d'):format(specialNum)
-      else
-        self.statusText = L('Pet Action')
-      end
-      self.bindingText = kb.BindingString(GetBindingKey(self.command))
-    else
-      self.statusText = '|cFF00FF00'.. (BUTTON_HEADERS[self.actionType] and BUTTON_HEADERS[self.actionType] or self.actionType) .. '|r'
-      self.bindingText = kb.BindingString(GetBindingKey(self.command))
-    end
-
-    local locked, layer = kb.IsCommandBound(self)
-    if locked then
-      self.icon:SetAlpha(0.5)
-    else
-      self.icon:SetAlpha(1)
-    end
-
-
-    if self.actionType == 'spell' then
-      self.icon:SetTexture(GetSpellTexture(self.actionID))
-    end
-  else
-    if kb.saveTarget == self then
-      kb.DeactivateSlot(self)
-    end
-
-    self.ignoreTexture:Hide()
-  end
-
-  if not self.isAvailable then
-    self.bind:SetTextColor(0.7,0.7,0.7,1)
-  else
-    self.bind:SetTextColor(1,1,1,1)
-  end
-
-
-  if kb.saveTarget and kb.saveTarget ~= self then
-    self:SetAlpha(0.25)
-  else
-
-    self:SetAlpha(1)
-  end
-
-
-  self.header:SetText(self.statusText)
-  self.bind:SetText(self.bindingText)
-  self.details:SetText(self.actionName)
-end
-
---- Resets button command
-kb.ReleaseSlot = function(self)
-  local slot = self:GetID()
-
-
-  if kb.currentProfile.buttons[slot] then
-    kb.currentProfile.buttons[slot] = nil
-  end
-  if self.command then
-    kb.currentProfile.commands[self.command] = nil
-  end
-  local talentName = self.actionID
-  if self.actionType == 'macro' then
-    talentName = GetMacroSpell(self.actionID)
-  end
-    -- remove any matching talent data
-  if talentName and kb.currentProfile.talents[talentName] then
-    kb.currentProfile.talents[talentName] = nil
-  end
-  local droppedKeys = {}
-
-  -- doing removal in second loop to avoid possible iterator shenanigans
-  for k,v in pairs(kb.currentProfile.bindings) do
-    if v == self.command then
-      tinsert(droppedKeys, k)
-    end
-  end
-  if #droppedKeys >=1 then
-    for i, k in ipairs(droppedKeys) do
-      kb.currentProfile.bindings[k] = nil
-    end
-  end
-
-  self.isAvailable = nil
-  self.isDynamic = nil
-  self.bindingText = nil
-  self.statusText = nil
-  self.command = nil
-  self.actionType = nil
-  self.actionID = nil
-  self.actionName = nil
-  self.pickupSlot = nil
-  self.pickupBook = nil
-  self.macroName = nil
-  self.profile = nil
-  self.icon:SetTexture(nil)
-  self.border:SetColorTexture(unpack(BORDER_UNASSIGNED))
-  self:EnableKeyboard(false)
-  self:SetScript('OnKeyDown', nil)
-  self.ignoreTexture:Hide()
-end
-
-kb.SetSlot = function(self, command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook)
-  local slot = self:GetID()
-  local isDynamic, isAvailable
-
-  print('|cFFFFFF00SetSlot|r:', self:GetID())
-  if command then
-
-    if actionType == 'spell' then
-      local professionNum, spellNum = command:match("profession_(%d)_(%d)")
-      if (professionNum and spellNum) then
-        isDynamic = 'profession'
-        local cacheInfo = kb.ProfessionCache[professionNum..'_'..spellNum]
-        if cacheInfo then
-          isAvailable = true
-          name = cacheInfo.spellName
-          icon = cacheInfo.icon
-          actionID = cacheInfo.spellID
-          self.profIndex = cacheInfo.profIndex
-          self.spellOffset = cacheInfo.spellOffset
-        else
-          print(professionNum, type(professionNum), PROFESSION_HEADERS[tonumber(professionNum)])
-          local profID = PROFESSION_HEADERS[tonumber(professionNum)]
-          if type(profID) == 'number' then
-            local pname, texture, _, _, numSpells, spellOffset = GetProfessionInfo(profID)
-            self.profIndex = profID
-          else
-            name = profID
-          end
-
-        end
-        print(' Special slot: |cFF00FFFFProfession|r', professionNum, spellNum, isDynamic, isAvailable)
-
-        self.professionNum = tonumber(professionNum)
-        self.spellNum = tonumber(spellNum)
-
-      else
-        if kb.TalentCache[actionID] then
-          isDynamic = 'talent'
-          print(' Special slot: |cFFBBFF00talent|r', name, isAvailable)
-        end
-
-        isAvailable = GetSpellInfo(name)
-      end
-    elseif actionType == 'petaction' then
-      isDynamic = 'petaction'
-      local specialType, specialNum = command:match(actionType..'_([%a%s]+)_(%d)')
-
-      if kb.PetCache.subtext[specialType] and kb.PetCache.subtext[specialType][tonumber(specialNum)] then
-        print('***dynamic pet skill', specialType, specialNum)
-        --[[ i, spellName, subText, spellID,  texture, specialNum[subText ]]
-        pickupSlot, name, specialType, actionID, icon, specialNum = unpack(kb.PetCache.subtext[specialType][tonumber(specialNum)])
-        pickupBook = BOOKTYPE_PET
-      end
-
-      isAvailable = (kb.PetCache.spellslot[name])
-
-
-    elseif actionType == 'macro' then
-      if not actionID then
-        actionID = GetMacroIndexByName(name)
-      end
-      isAvailable = true
-    else
-      --- Journal selections
-      -- todo: consider using the deep end of blizzard action bar instead
-      if not actionID then
-        actionID = command:match("^KeyBinderMacro:(.+)")
-      end
-      isAvailable = true
-    end
-
-    if isAvailable then
-      local oldCommand = command
-      macroName, macroText, command = kb.RegisterAction(actionType, actionID, name)
-      if oldCommand ~= command then
-        print('|cFFFF4400fixing command string', actionType, actionID, name)
-        kb.currentProfile.bound[oldCommand] = nil
-        kb.currentProfile.bound[command] = slot
-        for k,v in pairs(kb.currentProfile.bindings) do
-          if v == oldCommand then
-            kb.currentProfile.bindings[k] = command
-          end
-        end
-      end
-      kb.LoadBinding(command, name, icon, actionType, actionID, macroName, macroText)
-    end
-
-
-    actionID = actionID or 0
-    self:EnableKeyboard(true)
-    print(' |cFF00FF00kb.currentProfile.buttons['..slot..'] |cFF00FFFF=|r |cFF00FFFF"'.. command.. '"|r |cFF00FF00"'.. name.. '"|r |cFFFFFF00icon:'.. tostring(icon) .. '|r |cFFFF8800"'.. actionType, '"|r |cFFFF0088id:'.. actionID ..'|r |cFF00FF00"'.. macroName .. '"|r')
-    kb.currentProfile.buttons[slot] = {command, name, icon, actionType, actionID, macroName, macroText, pickupSlot, pickupBook }
-
-
-    -- Clean up conflicting entries for loaded button
-    local previous = kb.currentProfile.commands[command]
-    if previous ~= slot and kb.buttons[previous] then
-      kb.ReleaseSlot(kb.buttons[previous])
-    end
-
-    if not (kb.IsCommandBound(self, command) or kb.currentProfile.bound[command]) then
-
-      local binds = {GetBindingKey(command) }
-        if #binds >= 1 then
-        kb:print('Recovered key binding for', name)
-        for i, key in ipairs(binds) do
-          kb.currentProfile.bindings[key] = command
-          kb.currentProfile.bound[command] = true
-        end
-      end
-    end
-
-
-    kb.currentProfile.commands[command] = slot
-
-  end
-
-  self.isAvailable = isAvailable
-  self.isDynamic = isDynamic
-
-  self.pickupSlot = pickupSlot
-  self.pickupBook = pickupBook
-  self.macroText = macroText
-  self.macroName = macroName
-  self.actionType = actionType
-  self.actionID = actionID
-  self.actionName = name
-  self.command = command
-  self.icon:SetTexture(icon)
-  self.profile = kb.db.bindMode
-  self:RegisterForDrag('LeftButton')
-end
-
-kb.GetCommandAction = function(command)
-  for i, data in ipairs(kb.loadedProfiles) do
-    if data.commands[command] then
-      if data.buttons[data.commands[command]] then
-        local _, _, _, actionType, actionID = unpack(data.buttons[data.commands[command]])
-        return actionType, actionID
-      end
-    end
-  end
-end
-
--- a/SkeletonKey/SkeletonKey.iml	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,12 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<module type="LUA_MODULE" version="4">
-  <component name="NewModuleRootManager" inherit-compiler-output="true">
-    <exclude-output />
-    <content url="file://$MODULE_DIR$/../LibKraken" />
-    <content url="file://$MODULE_DIR$">
-      <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
-    </content>
-    <orderEntry type="inheritedJdk" />
-    <orderEntry type="sourceFolder" forTests="false" />
-  </component>
-</module>
\ No newline at end of file
--- a/SkeletonKey/SkeletonKey.lua	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,327 +0,0 @@
---------------------------------------------
--- SkeletonKey
--- Krakyn-Mal'Ganis
--- @project-revision@ @project-hash@
--- @file-revision@ @file-hash@
--- Created: 6/16/2016 3:47 AM
---------------------------------------------
--- Header script
-
-local _
-local kb, print = LibStub("LibKraken").register(KeyBinder)
-kb.L = setmetatable({}, {
-  __call = function(t, k, ...) return format(t[k] or k, ...) end
-})
-local L = kb.L
-
---- Caps Lock literals
-L.BINDING_ASSIGNED = '|cFF00FF00%s|r assigned to |cFFFFFF00%s|r (%s).'
-L.BINDING_REMOVED = '|cFFFFFF00%s|r (|cFF00FFFF%s|r) unbound.'
-L.BINDING_FAILED_PROTECTED = '|cFFFF4400Cannot use |r|cFF00FF00%s|r|cFFFF4400 (currently |cFFFFFF00%s|r|cFFFF4400). Uncheck "Safety" to ignore this restraint.|r'
-
-
-local BINDING_TYPE_SPECIALIZATION = 3
-local BINDING_TYPE_CHARACTER = 2
-local BINDING_TYPE_GLOBAL = 1
-kb.configTitle = {
-  [BINDING_TYPE_GLOBAL] = L('Global Binds'),
-  [BINDING_TYPE_CHARACTER] = L('Character: %%s'),
-  [BINDING_TYPE_SPECIALIZATION] = L('Specialization: %%s')
-}
-kb.configDescription = {
-  [BINDING_TYPE_GLOBAL] = L('The bindings are applied globally.'),
-  [BINDING_TYPE_CHARACTER] = L('Applied when you log onto this character.'),
-  [BINDING_TYPE_SPECIALIZATION] = L('Applied when you select this specialization.'),
-}
-
-kb.ChangedBindings = {}
-kb.SystemBindings = {}
-kb.ActionTypes = {}
-kb.TalentBindings = {}
-kb.PetCache = {
-  spell = {},
-  spellslot = {},
-  action = {},
-  special = {},
-  subtext = {}
-}
-kb.TalentCache = {}
-kb.ProfessionCache = {}
-kb.pendingCalls = {}
-kb.pendingAttributes = {}
-
-kb.configHeaders = {}
-kb.loadedProfiles = {}
-kb.orderedProfiles = {}
-kb.buttons = {}
-kb.macros = {}
-kb.bindings = {}
-kb.petFrames = {} -- pet data is slightly delayed, their buttons are indexed here so they can be refreshed
-kb.talentFrames = {}
-kb.professionFrames = {}
-
--- these are sent to plugin
-
-
-local db
-local _G = _G
-local UnitName, SelectedRealmName, InCombatLockdown, UnitClass = UnitName, SelectedRealmName, InCombatLockdown, UnitClass
-local tostring, select, tinsert, pairs = tostring, select, tinsert, pairs
-local concat, wipe = table.concat, table.wipe
-local classHeader, className, classID = '', '', 0
-
---- Control handles
-local saveButton, restoreButton, clearButton
-
---- Returns conflicting assignment and binding profiles for use in displaying confirmations
-kb.IsCommandBound = function(self, command)
-  local isAssigned, assignedBy = false, db.bindMode
-  local isBound, boundBy = false, db.bindMode
-  command = command or self.command
-  for i = 1, #kb.orderedProfiles do
-    local tier = kb.orderedProfiles[i]
-    if i ~= db.bindMode then
-
-      if tier.commands[command] then
-        isAssigned = true
-        assignedBy = i
-      end
-      if tier.bound[command] then
-        isBound = true
-        boundBy = i
-      end
-
-
-      --print(' *', configHeaders[i], tier.commands[command], tier.bound[command])
-
-      if isAssigned and isBound then
-        break
-      end
-    end
-
-  end
-
-  print('|cFFFFFF00IsCommandBound:|r', command,'|r [profile:', db.bindMode .. ']', isAssigned, isBound, assignedBy, boundBy)
-  return isAssigned, isBound, assignedBy, boundBy
-end
-
-local talentSpellHardCodes = {
-  [109248] = 'Binding Shot',
-}
-
---- Returns a value for use with Texture:SetDesaturated()
-kb.BindingIsLocked = function(key)
-  local success = false
-  for i = 1, db.bindMode-1 do
-    local tier = kb.orderedProfiles[i]
-    if tier.bindings[key] then
-      success = true
-      break
-    end
-  end
-  return success
-end
-
---- Translates GetBindingKey() results into a printable string.
-kb.BindingString = function(...)
-  local stack = {}
-  for i = 1, select('#', ...) do
-    local key = select(i, ...)
-    stack[i] = key:gsub('SHIFT', 's'):gsub('ALT', 'a'):gsub('CTRL', 'c'):gsub('SPACE', 'Sp'):gsub('BUTTON', 'M '):gsub('NUMPAD', '# ')
-  end
-
-  if #stack >= 1 then
-    return concat(stack, ',')
-  else
-    return nil
-  end
-end
-
-
-
-
-
-kb.Command = function(args, editor)
-  if args:match("import") then
-    kb.ImportCommmit(args)
-    return
-  elseif args:match("scan") then
-    kb.ImportScan(args)
-    kb.ui()
-    return
-  elseif args:match("load") then
-    kb:ApplyAllBindings()
-    return
-  end
-
-  if db.showUI then
-    db.showUI = false
-    kb:Hide()
-  else
-    db.showUI = true
-    if not InCombatLockdown() then
-      kb:print(L('Config frame opened.'))
-    else
-      kb:print(L('Config frame will open upon exiting combat.'))
-    end
-  end
-  kb.ui(true)
-end
-
-kb.InitProfile = function(profile, prototype)
-  if not profile then
-    profile = {}
-  end
-  if prototype then
-    print('appplying prototype', prototype)
-    for k,v in pairs(prototype) do
-      if not profile[k] then
-        profile[k] = v
-      end
-    end
-  end
-
-  profile.bound = profile.bound or {}
-  profile.buttons = profile.buttons or {}
-  profile.commands = profile.commands or {}
-  profile.bindings = profile.bindings or {}
-  profile.macros = profile.macros or {}
-  profile.talents = profile.talents or {}
-  return profile
-end
-
-kb.ResetProfile = function(profile, prototype)
-  if profile == kb.currentProfile then
-    for i, button in pairs(kb.buttons) do
-      kb.ReleaseSlot(button)
-    end
-  end
-  wipe(profile)
-  kb.InitProfile(profile, prototype)
-end
-
-
-
---- Handles constructing spec profiles as they are selected
-
-
---- Obtains profile data or creates the necessary tables
-kb.SelectProfileSet = function(name)
-
-  local defaultMode
-  --- General info
-  classHeader, className, classID = UnitClass('player')
-  print('|cFF00FF00profile:|r', name)
-  print('|cFF00FF00class:|r', UnitClass('player'))
-
-  defaultMode = BINDING_TYPE_GLOBAL
-  if db[name] then
-    defaultMode = BINDING_TYPE_CHARACTER
-    if db[name][kb.specInfo.id] then
-      defaultMode = BINDING_TYPE_SPECIALIZATION
-    end
-  end
-
-  db[name] = kb.InitProfile(db[name],
-    {
-      classHeader = classHeader,
-      className = className,
-      classID = classID
-    })
-  db[name][kb.specInfo.id] = kb.InitProfile(db[name][kb.specInfo.id],
-    {
-      specID = kb.specInfo.id,
-      specName = kb.specInfo.name
-    })
-
-  kb.loadedProfiles[BINDING_TYPE_GLOBAL] = db
-  kb.loadedProfiles[BINDING_TYPE_CHARACTER] = db[name]
-  kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] = db[name][kb.specInfo.id]
-  kb.orderedProfiles = {db, db[name], db[name][kb.specInfo.id]}
-
-  if (not db.bindMode) or (not kb.configTitle[db.bindMode]) then
-    print('fixing bad bindMode value, was', db.bindMode)
-    db.bindMode = defaultMode
-  end
-
-
-  print(BINDING_TYPE_GLOBAL)
-  kb.configHeaders[BINDING_TYPE_GLOBAL] = kb.configTitle[BINDING_TYPE_GLOBAL]
-  kb.configHeaders[BINDING_TYPE_CHARACTER] = kb.configTitle[BINDING_TYPE_CHARACTER]:format(UnitName('player', true))
-  kb.configHeaders[BINDING_TYPE_SPECIALIZATION] = kb.configTitle[BINDING_TYPE_SPECIALIZATION]:format(kb.specInfo.name)
-
-
-  setmetatable(kb.loadedProfiles[BINDING_TYPE_GLOBAL], {__tostring =function() return kb.configHeaders[BINDING_TYPE_GLOBAL] end})
-  setmetatable(kb.loadedProfiles[BINDING_TYPE_CHARACTER], {__tostring =function() return kb.configHeaders[BINDING_TYPE_CHARACTER] end})
-  setmetatable(kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION], {__tostring =function() return kb.configHeaders[BINDING_TYPE_SPECIALIZATION] end})
-
-  print('|cFF00FF00bindMode:|r', db.bindMode)
-  kb.currentProfile = kb.loadedProfiles[db.bindMode]
-  kb.currentHeader = kb.configHeaders[db.bindMode]
-end
-
-local scrollCache = {}
-kb.SelectTab = function(self)
-  scrollCache[db.bindMode] = kb.scrollOffset
-  db.bindMode = self:GetID()
-  kb.currentProfile = kb.loadedProfiles[self:GetID()]
-  kb.currentHeader = kb.configHeaders[db.bindMode]
-  kb.scrollOffset = scrollCache[db.bindMode] or 0
-  kb.ui(true)
-end
-
-kb.RevertBindings = function()
-  -- todo: reversion code
-end
-
-kb.ConfirmBindings = function()
-  kb.ApplyAllBindings()
-  kb.ui()
-  if #kb.pendingAttributes == 0 then
-    kb:print(L("Manual bindings update finished."))
-  else
-    kb:print(L("Manual update will complete upon exiting combat."))
-  end
-end
-
---- post ADDON_LOADED
-kb.variables = function()
-  _G.SkeletonKeyDB = kb.InitProfile(_G.SkeletonKeyDB, {})
-  kb.db = _G.SkeletonKeyDB
-  kb.playerName = UnitName('player')
-  kb.playerRealm = SelectedRealmName()
-  kb.profileName = kb.playerRealm .. '_' .. kb.playerName
-  db = kb.db
-
-  kb.UpdateSpecInfo()
-  kb.UpdateTalentInfo()
-  kb.SelectProfileSet(kb.profileName)
-  -- todo: redo import checking
-
-  kb.UpdateSystemBinds()
-  kb.ApplyAllBindings()
-
-  if not InCombatLockdown() then
-    kb.CreateHooks()
-  else
-    kb:print('Some functionality will not load until breaking combat.')
-    tinsert(kb.pendingCalls, kb.CreateHooks)
-  end
-
-
-  kb.ui(true)
-end
-
-
--- Volatiles Access
-kb.FormatActionID = function(actionType, actionID) return tostring(actionType) .. '_' .. tostring(actionID) end
-kb.GetBindings = function() return kb.bindings end
-kb.GetButtons = function() return kb.buttons end
-kb.GetCharacterProfile = function () return kb.loadedProfiles[BINDING_TYPE_CHARACTER] end
-kb.GetGlobalProfile = function () return kb.loadedProfiles[BINDING_TYPE_GLOBAL] end
-kb.GetSpecProfile = function () return kb.loadedProfiles[BINDING_TYPE_SPECIALIZATION] end
-
-
-SLASH_SKB1 = "/skb"
-SLASH_SKB2 = "/skeletonkey"
-SlashCmdList.SKB = kb.Command
--- a/SkeletonKey/SkeletonKey.toc	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-## Interface: 70100
-## Title: SkeletonKey
-## Notes: Key Bindings for dinosaurs
-## Author: Krakyn
-## Version: 1.0-@project-revision@
-## SavedVariables: SkeletonKeyDB
-## X-Category: Interface Enhancements
-## DefaultState: Enabled
-## LoadOnDemand: 0
-## OptionalDeps: LibStub, LibKraken, Devian
-
-libs\LibStub\LibStub.lua
-libs\LibKraken-1.0\LibKraken-1.0.xml
-SkeletonKey.xml
-SkeletonKey.lua
-ActionTemplates.lua
-DynamicTypes.lua
-BindingsFrame.lua
-KeyButton.lua
-
-Events.lua
-
-HotKey.lua
-Import.lua
\ No newline at end of file
--- a/SkeletonKey/SkeletonKey.xml	Tue Oct 25 12:36:53 2016 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,357 +0,0 @@
-<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">
-  <Button name="KeyBinderMacro" inherits="SecureActionButtonTemplate">
-    <Attributes>
-      <Attribute name="*type*" value="macro" />
-    </Attributes>
-  </Button>
-  <Button name="KeyBinderKey" inherits="SecureActionButtonTemplate" />
-  <CheckButton name="KeyButton" virtual="true">
-    <Size x="32" y="32" />
-    <Layers>
-      <Layer level="BACKGROUND">
-        <Texture parentKey="border" setAllPoints="true">
-          <Color a="1" r=".25" g=".25" b=".25" />
-        </Texture>
-      </Layer>
-      <Layer level="BORDER">
-
-        <Texture parentKey="bg">
-          <Anchors>
-            <Anchor point="TOPLEFT" x="2" y="-2" />
-            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
-          </Anchors>
-          <Color a="0.5" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-      <Layer level="ARTWORK">
-        <Texture setAllPoints="true" parentKey="icon">
-          <Anchors>
-            <Anchor point="TOPLEFT" x="2" y="-2" />
-            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
-          </Anchors>
-
-          <TexCoords left="0.1" right="0.9" top="0.1" bottom="0.9" />
-
-        </Texture>
-      </Layer>
-      <Layer level="OVERLAY">
-        <FontString inherits="NumberFontNormal" parentKey="header" wordwrap="false" justifyH="LEFT">
-
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="2" y="-2" />
-            <Anchor point="RIGHT" x="128" y="0" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="NumberFontNormal" parentKey="bind">
-          <Anchors>
-            <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="KTMacroButtonFont" parentKey="macro">
-          <Anchors>
-            <Anchor point="TOPLEFT" x="2" y="-2" />
-            <Anchor point="RIGHT" x="-2" y="0" />
-          </Anchors>
-        </FontString>
-        <FontString inherits="NumberFontNormal" parentKey="details" justifyH="LEFT">
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" x="0" y="-2" relativeKey="$parent.header" />
-            <Anchor point="RIGHT" x="128" y="0" />
-          </Anchors>
-        </FontString>
-
-        <Texture parentKey="ignoreTexture" file="Interface\PaperDollInfoFrame\UI-GearManager-LeaveItem-Transparent" hidden="true">
-          <Anchors>
-            <Anchor point="TOPLEFT" x="0" y="0" />
-            <Anchor point="BOTTOMRIGHT" x="0" y="0" />
-          </Anchors>
-        </Texture>
-      </Layer>
-    </Layers>
-    <HighlightTexture alphaMode="ADD">
-      <Anchors>
-        <Anchor point="TOPLEFT" x="2" y="-2" />
-        <Anchor point="BOTTOMRIGHT" x="-2" y="2" />
-      </Anchors>
-      <Color a="1" r="0.15" g="0.15" b="0.15" />
-    </HighlightTexture>
-  </CheckButton>
-
-  <Frame name="KeyBinder" parent="UIParent" hidden="true" clampedToScreen="true" movable="true" enableMouse="true">
-    <Anchors>
-      <Anchor point="TOP" y="-25" x="0" />
-    </Anchors>
-    <Size x="600" y="200" />
-    <KeyValues>
-      <KeyValue key="DEVIAN_PNAME" value="SkeletonKey"/>
-    </KeyValues>
-    <Scripts>
-      <OnLoad>
-        self:RegisterForDrag('LeftButton')
-      </OnLoad>
-      <OnShow>
-      </OnShow>
-      <OnDragStart>
-        self:StartMoving()
-      </OnDragStart>
-      <OnDragStop>
-        self:StopMovingOrSizing()
-      </OnDragStop>
-      <OnMouseWheel>
-        self:OnMouseWheel(delta)
-      </OnMouseWheel>
-      <OnHide>
-        self:OnHide()
-      </OnHide>
-    </Scripts>
-    <Layers>
-      <Layer level="BACKGROUND">
-
-        <Texture parentKey="info">
-          <Anchors>
-            <Anchor point="TOPLEFT" />
-            <Anchor point="RIGHT" />
-          </Anchors>
-          <Size y="42" />
-        </Texture>
-
-        <Texture parentKey="headerbg" alphaMode="MOD">
-          <Size y="32" />
-          <Anchors>
-            <Anchor point="TOPLEFT" />
-            <Anchor point="RIGHT" />
-          </Anchors>
-          <Color a="1" r="1" g="1" b="1" />
-          <Gradient orientation="VERTICAL">
-            <MinColor r="0" g="0" b="0"/>
-            <MaxColor r="1" g="1" b="1"/>
-          </Gradient>
-        </Texture>
-
-        <Texture parentKey="sourcesbg">
-          <Size x="100" />
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeKey="$parent.headerbg" />
-            <Anchor point="BOTTOM" />
-          </Anchors>
-          <Color a=".75" r="0" g="0" b="0" />
-        </Texture>
-
-
-
-        <Texture parentKey="profilebg">
-          <Size  y="102" />
-          <Anchors>
-            <Anchor point="TOP" relativePoint="BOTTOM" relativeKey="$parent.headerbg" />
-            <Anchor point="LEFT" relativePoint="RIGHT" relativeKey="$parent.sourcesbg" />
-            <Anchor point="RIGHT" />
-          </Anchors>
-          <Color a="1" r="0" g="0" b="0" />
-        </Texture>
-
-        <Texture parentKey="bg">
-          <Anchors>
-            <Anchor point="TOP" relativePoint="BOTTOM" relativeKey="$parent.profilebg" />
-            <Anchor point="LEFT" relativePoint="RIGHT" relativeKey="$parent.sourcesbg" />
-          </Anchors>
-          <Color a="0.5" r="0" g="0" b="0" />
-        </Texture>
-
-        <Texture parentKey="footer">
-          <Size y="52" />
-          <Anchors>
-            <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeKey="$parent.bg" x="0" y="0" />
-            <Anchor point="RIGHT"  />
-          </Anchors>
-          <Color a="1" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-      <Layer level="OVERLAY">
-
-        <FontString inherits="KTHeaderFont" text="Bindings" parentKey="header">
-          <Anchors>
-            <Anchor point="TOPLEFT" />
-          </Anchors>
-        </FontString>
-
-        <FontString parentKey="profiletext" inherits="KTHeader2Font" justifyH="LEFT" text="Foobar">
-          <Anchors>
-            <Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT" relativeKey="$parent.header" x="8" y="5" />
-          </Anchors>
-          <Color a="1" r="1" g="0.7" b="0" />
-        </FontString>
-
-        <FontString parentKey="statustext" inherits="NumberFont_Outline_Large" justifyH="LEFT" justifyV="TOP">
-          <Anchors>
-            <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" relativeKey="$parent.bg" x="12" y="7" />
-          </Anchors>
-        </FontString>
-
-
-        <FontString parentKey="bindingstext" inherits="NumberFont_Outline_Large" justifyH="RIGHT" justifyV="TOP">
-          <Anchors>
-            <Anchor point="BOTTOMRIGHT" relativePoint="TOPRIGHT" relativeKey="$parent.bg" x="-12" y="7" />
-          </Anchors>
-          <Color a="1" r="0" g="1" b="0" />
-        </FontString>
-
-        <FontString parentKey="savingText" inherits="KTHeaderFont" text="Press a key." />
-      </Layer>
-    </Layers>
-    <Frames>
-
-
-      <Frame name="KeyBinderTutorial" parent="KeyBinder">
-
-        <Size x="20" y="20" />
-        <Anchors>
-          <Anchor point="TOPLEFT" relativeKey="$parent.footer" x="2" y="-2" />
-          <Anchor point="TOPRIGHT" relativeKey="$parent.footer" x="-2" y="-2" />
-        </Anchors>
-        <Layers>
-          <Layer level="ARTWORK">
-            <Texture name="$parentMouseLeftClick" file="Interface\TutorialFrame\UI-TUTORIAL-FRAME">
-              <Size x="15" y="20"/>
-              <Anchors>
-                <Anchor point="TOPLEFT" />
-              </Anchors>
-              <TexCoords left="0.0019531" right="0.1484375" top="0.4257813" bottom="0.6210938"/>
-            </Texture>
-            <FontString inherits="KTLogString" text="to change a key binding.">
-              <Anchors>
-                <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeTo="$parentMouseLeftClick" x="2" y="0" />
-              </Anchors>
-            </FontString>
-
-            <Texture name="$parentMouseRightClick" file="Interface\TutorialFrame\UI-TUTORIAL-FRAME">
-              <Size x="15" y="20"/>
-              <Anchors>
-                <Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT" relativeTo="$parentMouseLeftClick" x="0" y="-2" />
-              </Anchors>
-              <TexCoords left="0.0019531" right="0.1484375" top="0.6269531" bottom="0.8222656"/>
-            </Texture>
-            <FontString inherits="KTLogString" text="to remove actions from profile.">
-              <Anchors>
-                <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeTo="$parentMouseRightClick" x="2" y="0" />
-              </Anchors>
-            </FontString>
-
-          </Layer>
-        </Layers>
-      </Frame>
-
-      <Button inherits="UIPanelCloseButton" parentKey="CloseButton">
-        <Anchors>
-          <Anchor point="TOPRIGHT" />
-        </Anchors>
-      </Button>
-
-      <Button name="$parentUnbindButton" hidden="true">
-        <Size x="48" y="24" />
-        <Layers>
-          <Layer level="OVERLAY">
-            <FontString inherits="KTUIPanelFont" text="Clear">
-              <Anchors>
-                <Anchor point="CENTER" />
-              </Anchors>
-            </FontString>
-          </Layer>
-        </Layers>
-        <NormalTexture>
-          <Color a="1" r=".7" g="0.12" b=".06" />
-        </NormalTexture>
-        <HighlightTexture alphaMode="ADD">
-
-          <Color a="1" r=".15" g="0.15" b=".15" />
-        </HighlightTexture>
-      </Button>
-
-
-      <CheckButton name="KeyBinderStickyMode" inherits="UICheckButtonTemplate">
-        <Size  y="32" x="32" />
-        <Layers>
-          <Layer level="OVERLAY">
-            <FontString parentKey="header" inherits="KTLogString" text="Sticky Mode:" />
-          </Layer>
-        </Layers>
-      </CheckButton>
-
-      <CheckButton name="KeyBinderHoverInput" inherits="UICheckButtonTemplate">
-        <Size  y="32" x="32" />
-        <Anchors>
-          <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" x="-4" y="0" relativeTo="KeyBinderStickyMode" />
-        </Anchors>
-        <Layers>
-          <Layer level="OVERLAY">
-            <FontString parentKey="header" inherits="KTLogString" text="Bind" />
-            <FontString inherits="KTHeaderFont" text="Settings">
-              <Anchors>
-                <Anchor point="BOTTOMLEFT" relativePoint="TOPLEFT" x="0" y="32" />
-              </Anchors>
-            </FontString>
-          </Layer>
-        </Layers>
-      </CheckButton>
-
-      <CheckButton name="KeyBinderProtectBindings" inherits="UICheckButtonTemplate">
-        <Size  y="32" x="32" />
-        <Anchors>
-          <Anchor point="TOPRIGHT" relativePoint="TOPLEFT" x="-4" y="0" relativeTo="KeyBinderHoverInput" />
-        </Anchors>
-        <Layers>
-          <Layer level="OVERLAY">
-            <FontString parentKey="header" inherits="KTLogString" text="Safe" />
-          </Layer>
-        </Layers>
-      </CheckButton>
-    </Frames>
-  </Frame>
-
-  <ScrollingMessageFrame hidden="true" fade="false"  name="KeyBinderImportLog" parent="KeyBinder" clampedToScreen="true" parentKey="ImportLog" insertMode="BOTTOM" maxLines="500">
-
-    <Scripts>
-      <OnLoad>
-        self:AddMessage('SkeletonKey import tool')
-      </OnLoad>
-      <OnMouseWheel>
-        if delta >= 0 then
-          if IsControlKeyDown() then
-            -- extremely janky but avoids having one line at the bottom
-            for i =1, self:GetMaxLines() do
-              self:ScrollUp()
-            end
-          end
-
-          return self:ScrollUp()
-        else
-
-          if IsControlKeyDown() then
-            return self:ScrollToBottom()
-          end
-
-          self:ScrollDown()
-        end
-      </OnMouseWheel>
-    </Scripts>
-    <Anchors>
-      <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" x="2" y="0" />
-      <Anchor point="BOTTOM" />
-    </Anchors>
-    <Size x="400" />
-    <Layers>
-      <Layer level="BACKGROUND">
-        <Texture setAllPoints="true">
-          <Color a="1" r="0" g="0" b="0" />
-        </Texture>
-      </Layer>
-    </Layers>
-    <FontString inherits="NumberFontNormal" justifyH="LEFT">
-      <Anchors>
-        <Anchor point="TOPLEFT" />
-      </Anchors>
-    </FontString>
-  </ScrollingMessageFrame>
-
-
-</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SystemBindings.xml	Wed Dec 28 16:31:15 2016 -0500
@@ -0,0 +1,14 @@
+<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="SystemBindings.lua" />
+
+  <Frame name="$parentSystemBindings" parent="SkeletonKey" mixin="SkeletonKeySystemBindingsMixin">
+    <Layers>
+      <Layer level="BACKGROUND">
+        <Texture parentKey="Background">
+          <Color a="1" r="1" g="0" b="0" />
+        </Texture>
+      </Layer>
+    </Layers>
+  </Frame>
+  </Ui>
\ No newline at end of file