Quantcast

Initial commit of Clique revamp

James Whitehead II [09-23-10 - 21:42]
Initial commit of Clique revamp
Filename
AddonCore.lua
COPYING
ClickCastTemplate.xml
Clique.lua
Clique.toc
Clique.xml
CliqueOptions.lua
CliqueUtils.lua
Dongle.lua
LICENSE
Localization.deDE.lua
Localization.enUS.lua
Localization.esES.lua
Localization.esMX.lua
Localization.frFR.lua
Localization.koKR.lua
Localization.ruRU.lua
Localization.zhCN.lua
Localization.zhTW.lua
images/CliqueIcon.tga
images/RadioChecked.tga
images/RadioEmpty.tga
images/backdrop.tga
images/borders.tga
images/footCorner.tga
images/footer.tga
images/headCorner.tga
images/header.tga
images/myborder.tga
readme.txt
diff --git a/AddonCore.lua b/AddonCore.lua
new file mode 100644
index 0000000..0808bdf
--- /dev/null
+++ b/AddonCore.lua
@@ -0,0 +1,77 @@
+local addonName, addon = ...
+
+-- Set global name of addon
+_G[addonName] = addon
+
+-- Extract version information from TOC file
+addon.version = GetAddOnMetadata(addonName, "Version")
+if addon.version == "@project-version" then
+    addon.version = "SCM"
+end
+
+-- Event registration and dispatch
+addon.eventFrame = CreateFrame("Frame", addonName .. "EventFrame", UIParent)
+local eventMap = {}
+
+function addon:RegisterEvent(event, handler)
+    assert(eventMap[event] == nil, "Attempt to re-register event: " .. tostring(event))
+    eventMap[event] = handler and handler or event
+    addon.eventFrame:RegisterEvent(event)
+end
+
+function addon:UnregisterEvent(event)
+    assert(type(event) == "string", "Invalid argument to 'UnregisterEvent'")
+    eventMap[event] = nil
+    addon.eventFrame:UnregisterEvent(event)
+end
+
+addon.eventFrame:SetScript("OnEvent", function(frame, event, ...)
+    local handler = eventMap[event]
+    local handler_t = type(handler)
+    if handler_t == "function" then
+        handler(event, ...)
+    elseif handler_t == "string" and addon[handler] then
+        addon[handler](addon, event, ...)
+    end
+end)
+
+addon:RegisterEvent("PLAYER_LOGIN", "Enable")
+addon:RegisterEvent("ADDON_LOADED", function(event, ...)
+    if ... == addonName then
+        addon:UnregisterEvent("ADDON_LOADED")
+        if type(addon["Initialize"]) == "function" then
+            addon["Initialize"](addon)
+        end
+
+        -- If this addon was loaded-on-demand, trigger 'Enable' as well
+        if IsLoggedIn() and type(addon["Enable"]) == "function" then
+            addon["Enable"](addon)
+        end
+    end
+end)
+
+-- Localization setup
+addon.L = addon.L or setmetatable({}, {
+    __index = function(t, k)
+        rawset(t, k, k)
+        return k
+    end,
+    __newindex = function(t, k, v)
+        if v == true then
+            rawset(t, k, k)
+        else
+            rawset(t, k, v)
+        end
+    end,
+})
+
+function addon:RegisterLocale(locale, tbl)
+    if locale == "enUS" or locale == GetLocale() then
+        for k,v in pairs(tbl) do
+            L[k] = v
+        end
+    end
+end
+
+-- Debug messages
+addon.debug = false
diff --git a/COPYING b/COPYING
old mode 100644
new mode 100755
index 0e2a027..969a1b8
--- a/COPYING
+++ b/COPYING
@@ -1,4 +1,4 @@
-The author supports and distributes this addon on
-http://www.wowinterface.com and requests that you refrain from posting
-this software in public locations.  This prevents confusion and
-difficulty when trying to provide support.
+The author supports and distributes this addon on http://www.wowinterface.com
+and requests that you refrain from posting this software in other public
+locations. This ensures the author is able to provide quality support to all
+users.
diff --git a/ClickCastTemplate.xml b/ClickCastTemplate.xml
new file mode 100644
index 0000000..a5cdfb5
--- /dev/null
+++ b/ClickCastTemplate.xml
@@ -0,0 +1,13 @@
+<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/
+    http://wowprogramming.com/FrameXML/UI.xsd">
+
+  <Button name="ClickCastUnitTemplate" virtual="true" inherits="SecureActionButtonTemplate,SecureHandlerEnterLeaveTemplate">
+      <Attributes>
+          <Attribute name="_onenter" type="string" value="local snippet = self:GetAttribute('clickcast_onenter'); if snippet then self:Run(snippet) end"/>
+          <Attribute name="_onleave" type="string" value="local snippet = self:GetAttribute('clickcast_onleave'); if snippet then self:Run(snippet) end"/>
+      </Attributes>
+  </Button>
+
+</Ui>
diff --git a/Clique.lua b/Clique.lua
old mode 100644
new mode 100755
index 85f3e8f..e47c372
--- a/Clique.lua
+++ b/Clique.lua
@@ -1,760 +1,44 @@
---[[---------------------------------------------------------------------------------
-  Clique by Cladhaire <cladhaire@gmail.com>
-----------------------------------------------------------------------------------]]
-
-Clique = {Locals = {}}
-
-assert(DongleStub, string.format("Clique requires DongleStub."))
-DongleStub("Dongle-1.2"):New("Clique", Clique)
-Clique.version = GetAddOnMetadata("Clique", "Version")
-if Clique.version == "wowi:revision" then Clique.version = "SVN" end
-
-local L = Clique.Locals
-
-function Clique:Enable()
-	-- Grab the localisation header
-	L = Clique.Locals
-	self.ooc = {}
-
-	self.defaults = {
-		profile = {
-			clicksets = {
-				[L.CLICKSET_DEFAULT] = {},
-				[L.CLICKSET_HARMFUL] = {},
-				[L.CLICKSET_HELPFUL] = {},
-				[L.CLICKSET_OOC] = {},
-			},
-			blacklist = {
-			},
-			tooltips = false,
-		},
-        char = {
-            switchSpec = false,
-            downClick = false,
-        },
-	}
-
-	self.db = self:InitializeDB("CliqueDB", self.defaults)
-	self.profile = self.db.profile
-	self.clicksets = self.profile.clicksets
-
-    self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
-
-	ClickCastFrames = ClickCastFrames or {}
-	self.ccframes = ClickCastFrames
-
-    local newindex = function(t,k,v)
-		if v == nil then
-			Clique:UnregisterFrame(k)
-			rawset(self.ccframes, k, nil)
-		else
-			Clique:RegisterFrame(k)
-			rawset(self.ccframes, k, v)
-		end
-    end
-
-	ClickCastFrames = setmetatable({}, {__newindex=newindex})
-
-    Clique:OptionsOnLoad()
-    Clique:EnableFrames()
-
-	-- Register for dongle events
-	self:RegisterMessage("DONGLE_PROFILE_CHANGED")
-	self:RegisterMessage("DONGLE_PROFILE_DELETED")
-	self:RegisterMessage("DONGLE_PROFILE_RESET")
-
-	self:RegisterEvent("PLAYER_REGEN_ENABLED")
-	self:RegisterEvent("PLAYER_REGEN_DISABLED")
-
-	self:RegisterEvent("LEARNED_SPELL_IN_TAB")
-    self:RegisterEvent("ACTIVE_TALENT_GROUP_CHANGED")
-    self:RegisterEvent("ADDON_LOADED")
-
-    -- Change to correct profile based on talent spec
-    if self.db.char.switchSpec then
-        self.silentProfile = true
-        self.talentGroup = GetActiveTalentGroup()
-        if self.talentGroup == 1 and self.db.char.primaryProfile then
-            self.db:SetProfile(self.db.char.primaryProfile)
-        elseif self.talentGroup == 2 and self.db.char.secondaryProfile then
-            self.db:SetProfile(self.db.char.secondaryProfile)
-        end
-        self.silentProfile = false
-    end
-
-	self:UpdateClicks()
-
-    -- Register all frames that snuck in before we did =)
-    for frame in pairs(self.ccframes) do
-		self:RegisterFrame(frame)
-    end
-
-    -- Securehook CreateFrame to catch any new raid frames
-    local raidFunc = function(type, name, parent, template)
-		if template == "RaidPulloutButtonTemplate" then
-			ClickCastFrames[getglobal(name.."ClearButton")] = true
-		end
-	end
-
-	local oldotsu = GameTooltip:GetScript("OnTooltipSetUnit")
-	if oldotsu then
-		GameTooltip:SetScript("OnTooltipSetUnit", function(...)
-			Clique:AddTooltipLines()
-			return oldotsu(...)
-		end)
-	else
-		GameTooltip:SetScript("OnTooltipSetUnit", function(...)
-			Clique:AddTooltipLines()
-		end)
-	end
-
-    hooksecurefunc("CreateFrame", raidFunc)
-
-	-- Create our slash command
-	self.cmd = self:InitializeSlashCommand("Clique commands", "CLIQUE", "clique")
-	self.cmd:RegisterSlashHandler("debug - Enables extra messages for debugging purposes", "debug", "ShowAttributes")
-	self.cmd:InjectDBCommands(self.db, "copy", "delete", "list", "reset", "set")
-	self.cmd:RegisterSlashHandler("tooltip - Enables binding lists in tooltips.", "tooltip", "ToggleTooltip")
-	self.cmd:RegisterSlashHandler("showbindings - Shows a window that contains the current bindings", "showbindings", "ShowBindings")
-
-	-- Place the Clique tab
-	self:LEARNED_SPELL_IN_TAB()
-
-    -- Register the arena frames, if they're already loaded
-    if IsAddOnLoaded("Blizzard_ArenaUI") then
-        self:EnableArenaFrames()
-    end
-end
-
-function Clique:EnableFrames()
-    local tbl = {
-		PlayerFrame,
-		PetFrame,
-		PartyMemberFrame1,
-		PartyMemberFrame2,
-		PartyMemberFrame3,
-		PartyMemberFrame4,
-		PartyMemberFrame1PetFrame,
-		PartyMemberFrame2PetFrame,
-		PartyMemberFrame3PetFrame,
-		PartyMemberFrame4PetFrame,
-		TargetFrame,
-		TargetFrameToT,
-		FocusFrame,
-        FocusFrameToT,
-        Boss1TargetFrame,
-        Boss2TargetFrame,
-        Boss3TargetFrame,
-        Boss4TargetFrame,
+--[[-------------------------------------------------------------------
+--  Clique - Copyright 2006-2010 - James N. Whitehead II
+--
+--  This is an updated version of the original 'Clique' addon
+--  designed to work better with multi-button mice, and those players
+--  who want to be able to bind keyboard combinations to enable
+--  hover-casting on unit frames.  It's a bit of a paradigm shift from
+--  the original addon, but should make a much simpler and more
+--  powerful addon.
+--
+--    * Any keyboard combination can be set as a binding.
+--    * Any mouse combination can be set as a binding.
+--    * The only types that are allowed are spells and macros.
+-------------------------------------------------------------------]]--
+
+local addonName, addon = ...
+local L = addon.L
+
+function addon:Initialize()
+end
+
+function addon:Enable()
+    print("Addon " .. addonName .. " enabled")
+    -- Make the options window a pushable panel window
+    UIPanelWindows["CliqueConfig"] = {
+        area = "left",
+        pushable = 1,
+        whileDead = 1,
     }
-
-    for i,frame in pairs(tbl) do
-		rawset(self.ccframes, frame, true)
-    end
-end
-
-function Clique:SpellBookButtonPressed(frame, button)
-    local id = SpellBook_GetSpellID(this:GetParent():GetID());
-    local texture = GetSpellTexture(id, SpellBookFrame.bookType)
-    local name, rank = GetSpellName(id, SpellBookFrame.bookType)
-
-    if rank == L.RACIAL_PASSIVE or rank == L.PASSIVE then
-		StaticPopup_Show("CLIQUE_PASSIVE_SKILL")
-		return
-    end
-
-    local type = "spell"
-
-	if self.editSet == self.clicksets[L.CLICKSET_HARMFUL] then
-		button = string.format("%s%d", "harmbutton", self:GetButtonNumber(button))
-	elseif self.editSet == self.clicksets[L.CLICKSET_HELPFUL] then
-		button = string.format("%s%d", "helpbutton", self:GetButtonNumber(button))
-	else
-		button = self:GetButtonNumber(button)
-	end
-
-	-- Clear the rank if "Show all spell ranks" is selected
-	if not GetCVarBool("ShowAllSpellRanks") then
-		rank = nil
-	end
-
-    -- Build the structure
-    local t = {
-		["button"] = button,
-		["modifier"] = self:GetModifierText(),
-		["texture"] = GetSpellTexture(id, SpellBookFrame.bookType),
-		["type"] = type,
-		["arg1"] = name,
-		["arg2"] = rank,
-    }
-
-    local key = t.modifier .. t.button
-
-    if self:CheckBinding(key) then
-		StaticPopup_Show("CLIQUE_BINDING_PROBLEM")
-	return
-    end
-
-    self.editSet[key] = t
-    self:ListScrollUpdate()
-	self:UpdateClicks()
-	-- We can only make changes when out of combat
-	self:PLAYER_REGEN_ENABLED()
-end
-
--- Player is LEAVING combat
-function Clique:PLAYER_REGEN_ENABLED()
-	self:ApplyClickSet(L.CLICKSET_DEFAULT)
-	self:RemoveClickSet(L.CLICKSET_HARMFUL)
-	self:RemoveClickSet(L.CLICKSET_HELPFUL)
-	self:ApplyClickSet(self.ooc)
-end
-
--- Player is ENTERING combat
-function Clique:PLAYER_REGEN_DISABLED()
-	self:RemoveClickSet(self.ooc)
-	self:ApplyClickSet(L.CLICKSET_DEFAULT)
-	self:ApplyClickSet(L.CLICKSET_HARMFUL)
-	self:ApplyClickSet(L.CLICKSET_HELPFUL)
-end
-
-function Clique:UpdateClicks()
-	local ooc = self.clicksets[L.CLICKSET_OOC]
-	local harm = self.clicksets[L.CLICKSET_HARMFUL]
-	local help = self.clicksets[L.CLICKSET_HELPFUL]
-
-    -- Since harm/help buttons take priority over any others, we can't
-    --
-    -- just apply the OOC set last.  Instead we use the self.ooc pseudo
-    -- set (which we build here) which contains only those help/harm
-    -- buttons that don't conflict with those defined in OOC.
-
-    self.ooc = table.wipe(self.ooc or {})
-
-    -- Create a hash map of the "taken" combinations
-    local takenBinds = {}
-
-    for name, entry in pairs(ooc) do
-        local key = string.format("%s:%s", entry.modifier, entry.button)
-        takenBinds[key] = true
-        table.insert(self.ooc, entry)
-    end
-
-    for name, entry in pairs(harm) do
-        local button = string.gsub(entry.button, "harmbutton", "")
-        local key = string.format("%s:%s", entry.modifier, button)
-        if not takenBinds[key] then
-            table.insert(self.ooc, entry)
-        end
-    end
-
-    for name, entry in pairs(help) do
-        local button = string.gsub(entry.button, "helpbutton", "")
-        local key = string.format("%s:%s", entry.modifier, button)
-        if not takenBinds[key] then
-            table.insert(self.ooc, entry)
-        end
-    end
-
-    self:UpdateTooltip()
-end
-
-function Clique:RegisterFrame(frame)
-	local name = frame:GetName()
-
-	if self.profile.blacklist[name] then
-		rawset(self.ccframes, frame, false)
-		return
-	end
-
-	if not ClickCastFrames[frame] then
-		rawset(self.ccframes, frame, true)
-		if CliqueTextListFrame then
-			Clique:TextListScrollUpdate()
-		end
-	end
-
-    -- Register AnyUp or AnyDown on this frame, depending on configuration
-    self:SetClickType(frame)
-
-	if frame:CanChangeAttribute() or frame:CanChangeProtectedState() then
-		if InCombatLockdown() then
-			self:ApplyClickSet(L.CLICKSET_DEFAULT, frame)
-			self:ApplyClickSet(L.CLICKSET_HELPFUL, frame)
-			self:ApplyClickSet(L.CLICKSET_HARMFUL, frame)
-		else
-			self:ApplyClickSet(L.CLICKSET_DEFAULT, frame)
-			self:ApplyClickSet(self.ooc, frame)
-		end
-	end
-end
-
-function Clique:ApplyClickSet(name, frame)
-	local set = self.clicksets[name] or name
-
-	if frame then
-		for modifier,entry in pairs(set) do
-			self:SetAttribute(entry, frame)
-		end
-	else
-		for modifier,entry in pairs(set) do
-			self:SetAction(entry)
-		end
-	end
-end
-
-function Clique:RemoveClickSet(name, frame)
-	local set = self.clicksets[name] or name
-
-	if frame then
-		for modifier,entry in pairs(set) do
-			self:DeleteAttribute(entry, frame)
-		end
-	else
-		for modifier,entry in pairs(set) do
-			self:DeleteAction(entry)
-		end
-	end
-end
-
-function Clique:UnregisterFrame(frame)
-	assert(not InCombatLockdown(), "An addon attempted to unregister a frame from Clique while in combat.")
-	for name,set in pairs(self.clicksets) do
-		for modifier,entry in pairs(set) do
-			self:DeleteAttribute(entry, frame)
-		end
-	end
-end
-
-function Clique:DONGLE_PROFILE_CHANGED(event, db, parent, svname, profileKey)
-	if db == self.db then
-        if not self.silentProfile then
-            self:PrintF(L.PROFILE_CHANGED, profileKey)
-        end
-
-		for name,set in pairs(self.clicksets) do
-			self:RemoveClickSet(set)
-		end
-		self:RemoveClickSet(self.ooc)
-
-		self.profile = self.db.profile
-		self.clicksets = self.profile.clicksets
-		self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
-		self.profileKey = profileKey
-
-		-- Refresh the profile editor if it exists
-		self.textlistSelected = nil
-		self:TextListScrollUpdate()
-		self:ListScrollUpdate()
-		self:UpdateClicks()
-
-		self:PLAYER_REGEN_ENABLED()
-	end
-end

-function Clique:DONGLE_PROFILE_RESET(event, db, parent, svname, profileKey)
-	if db == self.db then
-		for name,set in pairs(self.clicksets) do
-			self:RemoveClickSet(set)
-		end
-		self:RemoveClickSet(self.ooc)
-
-		self.profile = self.db.profile
-		self.clicksets = self.profile.clicksets
-		self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
-		self.profileKey = profileKey
-
-		-- Refresh the profile editor if it exists
-		self.textlistSelected = nil
-		self:TextListScrollUpdate()
-		self:ListScrollUpdate()
-		self:UpdateClicks()
-
-		self:PLAYER_REGEN_ENABLED()
-		self:Print(L.PROFILE_RESET, profileKey)
-	end
-end
-
-
-function Clique:DONGLE_PROFILE_DELETED(event, db, parent, svname, profileKey)
-	if db == self.db then
-		self:PrintF(L.PROFILE_DELETED, profileKey)
-
-		self.textlistSelected = nil
-		self:TextListScrollUpdate()
-		self:ListScrollUpdate()
-	end
-end
-
-function Clique:SetAttribute(entry, frame)
-	local name = frame:GetName()
-
-	if	self.profile.blacklist and self.profile.blacklist[name] then
-		return
-	end
-
-	-- Set up any special attributes
-	local type,button,value
-
-	if not tonumber(entry.button) then
-		type,button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
-		frame:SetAttribute(entry.modifier..entry.button, type..button)
-		assert(frame:GetAttribute(entry.modifier..entry.button, type..button))
-		button = string.format("-%s%s", type, button)
-	end
-
-	button = button or entry.button
-
-	if entry.type == "actionbar" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
-	elseif entry.type == "action" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
-		if entry.arg2 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg2)
-		end
-	elseif entry.type == "pet" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."action"..button, entry.arg1)
-		if entry.arg2 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg2)
-		end
-	elseif entry.type == "spell" then
-		local rank = entry.arg2
-		local cast
-		if rank then
-			if tonumber(rank) then
-				-- The rank is a number (pre-2.3) so fill in the format
-				cast = L.CAST_FORMAT:format(entry.arg1, rank)
-			else
-				-- The whole rank string is saved (post-2.3) so use it
-				cast = string.format("%s(%s)", entry.arg1, rank)
-			end
-		else
-			cast = entry.arg1
-		end
-
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."spell"..button, cast)
-
-		frame:SetAttribute(entry.modifier.."bag"..button, entry.arg2)
-		frame:SetAttribute(entry.modifier.."slot"..button, entry.arg3)
-		frame:SetAttribute(entry.modifier.."item"..button, entry.arg4)
-		if entry.arg5 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg5)
-		end
-	elseif entry.type == "item" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."bag"..button, entry.arg1)
-		frame:SetAttribute(entry.modifier.."slot"..button, entry.arg2)
-		frame:SetAttribute(entry.modifier.."item"..button, entry.arg3)
-		if entry.arg4 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg4)
-		end
-	elseif entry.type == "macro" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		if entry.arg1 then
-			frame:SetAttribute(entry.modifier.."macro"..button, entry.arg1)
-		else
-			local unit = SecureButton_GetModifiedUnit(frame, entry.modifier.."unit"..button)
-			local macro = tostring(entry.arg2)
-			if unit and macro then
-				macro = macro:gsub("target%s*=%s*clique", "target="..unit)
-			end
-
-			frame:SetAttribute(entry.modifier.."macro"..button, nil)
-			frame:SetAttribute(entry.modifier.."macrotext"..button, macro)
-		end
-	elseif entry.type == "stop" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-	elseif entry.type == "target" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		if entry.arg1 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
-		end
-	elseif entry.type == "focus" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		if entry.arg1 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
-		end
-	elseif entry.type == "assist" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		if entry.arg1 then
-			frame:SetAttribute(entry.modifier.."unit"..button, entry.arg1)
-		end
-	elseif entry.type == "click" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-		frame:SetAttribute(entry.modifier.."clickbutton"..button, getglobal(entry.arg1))
-	elseif entry.type == "menu" then
-		frame:SetAttribute(entry.modifier.."type"..button, entry.type)
-	end
+    self:InitializeDatabase()
 end

-function Clique:DeleteAttribute(entry, frame)
-	local name = frame:GetName()
-	if	self.profile.blacklist and self.profile.blacklist[name] then
-		return
-	end
-
-	local type,button,value
-
-	if not tonumber(entry.button) then
-		type,button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
-		frame:SetAttribute(entry.modifier..entry.button, nil)
-		button = string.format("-%s%s", type, button)
-	end
-
-	button = button or entry.button
-
-	entry.delete = true
-
-	frame:SetAttribute(entry.modifier.."type"..button, nil)
-	frame:SetAttribute(entry.modifier..entry.type..button, nil)
-end
-
-function Clique:SetAction(entry)
-	for frame,enabled in pairs(self.ccframes) do
-		if enabled then
-			self:SetAttribute(entry, frame)
-		end
-	end
-end
-
-function Clique:DeleteAction(entry)
-	for frame in pairs(self.ccframes) do
-		self:DeleteAttribute(entry, frame)
-	end
-end
-
-function Clique:ShowAttributes()
-	self:Print("Enabled enhanced debugging.")
-	PlayerFrame:SetScript("OnAttributeChanged", function(...) self:Print(...) end)
-	self:UnregisterFrame(PlayerFrame)
-	self:RegisterFrame(PlayerFrame)
-end
-
-local tt_ooc = {}
-local tt_help = {}
-local tt_harm = {}
-local tt_default = {}
-
-function Clique:UpdateTooltip()
-	local ooc = self.ooc
-	local default = self.clicksets[L.CLICKSET_DEFAULT]
-	local harm = self.clicksets[L.CLICKSET_HARMFUL]
-	local help = self.clicksets[L.CLICKSET_HELPFUL]
-
-	for k,v in pairs(tt_ooc) do tt_ooc[k] = nil end
-	for k,v in pairs(tt_help) do tt_help[k] = nil end
-	for k,v in pairs(tt_harm) do tt_harm[k] = nil end
-	for k,v in pairs(tt_default) do tt_default[k] = nil end
-
-	-- Build the ooc lines, which includes both helpful and harmful
-	for k,v in pairs(ooc) do
-		local button = self:GetButtonText(v.button)
-		local mod = string.format("%s%s", v.modifier or "", button)
-		local action = string.format("%s (%s)", v.arg1 or "", v.type)
-		table.insert(tt_ooc, {mod = mod, action = action})
-	end
-
-	-- Build the default lines
-	for k,v in pairs(default) do
-		local button = self:GetButtonText(v.button)
-		local mod = string.format("%s%s", v.modifier or "", button)
-		local action = string.format("%s (%s)", v.arg1 or "", v.type)
-		table.insert(tt_default, {mod = mod, action = action})
-	end
-
-	-- Build the harm lines
-	for k,v in pairs(harm) do
-		local button = self:GetButtonText(v.button)
-		local mod = string.format("%s%s", v.modifier or "", button)
-		local action = string.format("%s (%s)", v.arg1 or "", v.type)
-		table.insert(tt_harm, {mod = mod, action = action})
-	end
-
-	-- Build the help lines
-	for k,v in pairs(help) do
-		local button = self:GetButtonText(v.button)
-		local mod = string.format("%s%s", v.modifier or "", button)
-		local action = string.format("%s (%s)", v.arg1 or "", v.type)
-		table.insert(tt_help, {mod = mod, action = action})
-	end
-
-	local function sort(a,b)
-		return a.mod < b.mod
-	end
-
-	table.sort(tt_ooc, sort)
-	table.sort(tt_default, sort)
-	table.sort(tt_harm, sort)
-	table.sort(tt_help, sort)
-end
-
-function Clique:AddTooltipLines()
-	if not self.profile.tooltips then return end
-
-	local frame = GetMouseFocus()
-	if not frame then return end
-	if not self.ccframes[frame] then return end
-
-	-- Add a buffer line
-	GameTooltip:AddLine(" ")
-	if UnitAffectingCombat("player") then
-		if #tt_default ~= 0 then
-			GameTooltip:AddLine("Default bindings:")
-			for k,v in ipairs(tt_default) do
-				GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-			end
-		end
-
-		if #tt_help ~= 0 and not UnitCanAttack("player", "mouseover") then
-			GameTooltip:AddLine("Helpful bindings:")
-			for k,v in ipairs(tt_help) do
-				GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-			end
-		end
-
-		if #tt_harm ~= 0 and UnitCanAttack("player", "mouseover") then
-			GameTooltip:AddLine("Hostile bindings:")
-			for k,v in ipairs(tt_harm) do
-				GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-			end
-		end
-	else
-		if #tt_ooc ~= 0 then
-			GameTooltip:AddLine("Out of combat bindings:")
-			for k,v in ipairs(tt_ooc) do
-				GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-			end
-		end
-	end
-end
-
-function Clique:ToggleTooltip()
-	self.profile.tooltips = not self.profile.tooltips
-	self:PrintF("Listing of bindings in tooltips has been %s",
-	self.profile.tooltips and "Enabled" or "Disabled")
-end
-
-function Clique:ShowBindings()
-	if not CliqueTooltip then
-		CliqueTooltip = CreateFrame("GameTooltip", "CliqueTooltip", UIParent, "GameTooltipTemplate")
-		CliqueTooltip:SetPoint("CENTER", 0, 0)
-		CliqueTooltip.close = CreateFrame("Button", nil, CliqueTooltip)
-		CliqueTooltip.close:SetHeight(32)
-		CliqueTooltip.close:SetWidth(32)
-		CliqueTooltip.close:SetPoint("TOPRIGHT", 1, 0)
-		CliqueTooltip.close:SetScript("OnClick", function()
-			CliqueTooltip:Hide()
-		end)
-		CliqueTooltip.close:SetNormalTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Up")
-		CliqueTooltip.close:SetPushedTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Down")
-		CliqueTooltip.close:SetHighlightTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Highlight")
-
-		CliqueTooltip:EnableMouse()
-		CliqueTooltip:SetMovable()
-		CliqueTooltip:SetPadding(16)
-		CliqueTooltip:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b);
-		CliqueTooltip:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b);
-
-		CliqueTooltip:RegisterForDrag("LeftButton")
-		CliqueTooltip:SetScript("OnDragStart", function(self)
-			self:StartMoving()
-		end)
-		CliqueTooltip:SetScript("OnDragStop", function(self)
-			self:StopMovingOrSizing()
-			ValidateFramePosition(self)
-		end)
-		CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE")
-	end
-
-	if not CliqueTooltip:IsShown() then
-		CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE")
-	end
-
-	-- Actually fill it with the bindings
-	CliqueTooltip:SetText("Clique Bindings")
-
-	if #tt_default > 0 then
-		CliqueTooltip:AddLine(" ")
-		CliqueTooltip:AddLine("Default bindings:")
-		for k,v in ipairs(tt_default) do
-			CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-		end
-	end
-
-	if #tt_help > 0 then
-		CliqueTooltip:AddLine(" ")
-		CliqueTooltip:AddLine("Helpful bindings:")
-		for k,v in ipairs(tt_help) do
-			CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-		end
-	end
-
-	if #tt_harm > 0 then
-		CliqueTooltip:AddLine(" ")
-		CliqueTooltip:AddLine("Hostile bindings:")
-		for k,v in ipairs(tt_harm) do
-			CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-		end
-	end
-
-	if #tt_ooc > 0 then
-		CliqueTooltip:AddLine(" ")
-		CliqueTooltip:AddLine("Out of combat bindings:")
-		for k,v in ipairs(tt_ooc) do
-			CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
-		end
-	end
-
-	CliqueTooltip:Show()
-end
-
-function Clique:ACTIVE_TALENT_GROUP_CHANGED(event, newGroup, prevGroup)
-    if self.db.char.switchSpec then
-        self:Print("Detected a talent spec change, changing profile")
-        if newGroup == 1 and self.db.char.primaryProfile then
-            self.db:SetProfile(self.db.char.primaryProfile)
-        elseif newGroup == 2 and self.db.char.secondaryProfile then
-            self.db:SetProfile(self.db.char.secondaryProfile)
-        end
-        if CliqueFrame then
-            CliqueFrame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile));
-        end
-    end
-end
-
-function Clique:SetClickType(frame)
-    local clickType = Clique.db.char.downClick and "AnyDown" or "AnyUp"
-    if frame then
-        frame:RegisterForClicks(clickType)
-    else
-        for frame, enabled in pairs(self.ccframes) do
-            if enabled then
-                frame:RegisterForClicks(clickType)
-            end
-        end
-    end
-end
-
-function Clique:EnableArenaFrames()
-    local arenaFrames = {
-        ArenaEnemyFrame1,
-        ArenaEnemyFrame2,
-        ArenaEnemyFrame3,
-        ArenaEnemyFrame4,
-        ArenaEnemyFrame5,
+function addon:InitializeDatabase()
+    -- Force a clean database everytime
+    self.profile = {
+        binds = {},
     }
-
-    for idx,frame in ipairs(arenaFrames) do
-        rawset(self.ccframes, frame, true)
-    end
 end

-function Clique:ADDON_LOADED(event, addon)
-    if addon == "Blizzard_ArenaUI" then
-        self:EnableArenaFrames()
-    end
+SLASH_CLIQUE1 = "/clique"
+SlashCmdList["CLIQUE"] = function(msg, editbox)
+    ShowUIPanel(CliqueConfig)
 end
diff --git a/Clique.toc b/Clique.toc
old mode 100644
new mode 100755
index 99ec210..344b7f4
--- a/Clique.toc
+++ b/Clique.toc
@@ -1,22 +1,17 @@
-## Interface: 30300
+## Interface: 40000
 ## Title: Clique
 ## Author: Cladhaire
-## Version: wowi:revision
+## Version: @project-version@
 ## Notes: Simply powerful click-casting interface
 ## SavedVariables: CliqueDB
 ## OptionalDeps: Dongle

-Dongle.lua
+AddonCore.lua
+Localization.core.lua
+Localization.enUS.lua
+
 Clique.xml
+
+CliqueUtils.lua
 Clique.lua
-Localization.enUS.lua
-Localization.esES.lua
-Localization.esMX.lua
-Localization.frFR.lua
-Localization.deDE.lua
-Localization.koKR.lua
-Localization.ruRU.lua
-Localization.zhCN.lua
-Localization.zhTW.lua
 CliqueOptions.lua
-CliqueUtils.lua
diff --git a/Clique.xml b/Clique.xml
old mode 100644
new mode 100755
index 92b29f6..ca30933
--- a/Clique.xml
+++ b/Clique.xml
@@ -1,96 +1,321 @@
-<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">
-      <CheckButton name="CliqueIconTemplate" virtual="true">
-                <Size>
-                        <AbsDimension x="36" y="36"/>
-                </Size>
-                <Layers>
-                        <Layer level="BACKGROUND">
-                                <Texture file="Interface\Buttons\UI-EmptySlot-Disabled">
-                                        <Size>
-                                                <AbsDimension x="64" y="64"/>
-                                        </Size>
-                                        <Anchors>
-                                                <Anchor point="CENTER">
-                                                        <Offset>
-                                                                <AbsDimension x="0" y="-1"/>
-                                                        </Offset>
-                                                </Anchor>
-                                        </Anchors>
-                                </Texture>
-                        </Layer>
-                </Layers>
-                <NormalTexture name="$parentIcon">
-                        <Size>
-                                <AbsDimension x="36" y="36"/>
-                        </Size>
+<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/
+    http://wowprogramming.com/FrameXML/UI.xsd">
+
+    <Button name="CliqueRowTemplate" enableMouse="true" virtual="true">
+        <Size x="298" y="17"/>
+        <Layers>
+            <Layer level="BORDER">
+                <Texture name="$parentIcon" parentKey="icon"
+                    file="Interface\Icons\Ability_Druid_Nourish">
+                    <Size x="14" y="14"/>
+                    <Anchors>
+                        <Anchor point="TOPLEFT">
+                            <Offset>
+                                <AbsDimension x="5" y="0"/>
+                            </Offset>
+                        </Anchor>
+                    </Anchors>
+                </Texture>
+                <FontString name="$parentName" parentKey="name"
+                    inherits="GameFontHighlightSmall" justifyH="LEFT" text="Healing Touch">
+                    <Size>
+                        <AbsDimension x="175" y="14"/>
+                    </Size>
+                    <Anchors>
+                        <Anchor point="LEFT" relativeTo="$parentIcon" relativePoint="RIGHT">
+                            <Offset>
+                                <AbsDimension x="3" y="0"/>
+                            </Offset>
+                        </Anchor>
+                    </Anchors>
+                </FontString>
+                <!--
+                <FontString name="$parentType" parentKey="type"
+                    inherits="GameFontHighlightSmall" justifyH="CENTER" text="Spell">
+                    <Size>
+                        <AbsDimension x="40" y="14"/>
+                    </Size>
+                    <Anchors>
+                        <Anchor point="LEFT" relativeTo="$parentName" relativePoint="RIGHT">
+                            <Offset>
+                                <AbsDimension x="0" y="0"/>
+                            </Offset>
+                        </Anchor>
+                    </Anchors>
+                </FontString>
+                -->
+                <FontString name="$parentBind" parentKey="bind"
+                    inherits="GameFontHighlightSmall" justifyH="RIGHT" text="Alt-LeftClick">
+                    <Size>
+                        <AbsDimension x="115" y="14"/>
+                    </Size>
+                    <Anchors>
+                        <Anchor point="LEFT" relativeTo="$parentName" relativePoint="RIGHT">
+                            <Offset>
+                                <AbsDimension x="0" y="0"/>
+                            </Offset>
+                        </Anchor>
+                    </Anchors>
+                </FontString>
+            </Layer>
+        </Layers>
+        <Scripts>
+            <OnMouseWheel>
+                local slider = CliqueConfig.page1.slider
+                slider:SetValue(slider:GetValue() - delta)
+            </OnMouseWheel>
+        </Scripts>
+        <HighlightTexture setAllPoints="true" file="Interface\QuestFrame\UI-QuestTitleHighlight" alphaMode="ADD">
+            <Size x="315" y="14"/>
+            <Anchors>
+                <Anchor point="TOPLEFT">
+                    <Offset x="-1" y="0"/>
+                </Anchor>
+            </Anchors>
+        </HighlightTexture>
+    </Button>
+
+    <Button name="CliqueColumnTemplate" inherits="WhoFrameColumnHeaderTemplate" virtual="true">
+        <Scripts>
+            <OnClick>
+                PlaySound("igMainMenuOptionCheckBoxOn");
+            </OnClick>
+        </Scripts>
+    </Button>
+
+    <Button name="CliqueSpellbookButtonTemplate" setAllPoints="true" hidden="true" virtual="true">
+        <Scripts>
+            <OnEnter>
+                local parent = self:GetParent()
+                if parent:IsEnabled() == 1 then
+                    SpellButton_OnEnter(parent)
+                else
+                    self:GetHighlightTexture():Hide()
+                end
+
+                CliqueConfig:Spellbook_EnableKeyboard(self, motion)
+            </OnEnter>
+            <OnLeave>
+                GameTooltip:Hide()
+                CliqueConfig:Spellbook_DisableKeyboard(self, motion)
+            </OnLeave>
+            <OnClick>
+                CliqueConfig:Spellbook_OnBinding(self, button)
+            </OnClick>
+            <OnKeyDown>
+                CliqueConfig:Spellbook_OnBinding(self, key)
+            </OnKeyDown>
+            <OnMouseWheel>
+                local button = (delta > 0) and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
+                CliqueConfig:Spellbook_OnBinding(self, button)
+            </OnMouseWheel>
+        </Scripts>
+        <NormalTexture file="Interface\BUTTONS/GLOWSTAR">
+            <Size x="16" y="16"/>
+            <Anchors>
+                <Anchor point="BOTTOMRIGHT">
+                    <Offset x="-3" y="3"/>
+                </Anchor>
+            </Anchors>
+        </NormalTexture>
+        <HighlightTexture file="Interface\BUTTONS\ButtonHilight-Square" alphaMode="ADD"/>
+    </Button>
+
+    <!-- Main configuration frame -->
+    <Frame name="CliqueConfig" inherits="ButtonFrameTemplate" parent="UIParent" hidden="true">
+        <Size>
+            <AbsDimension x="333" y="475"/>
+        </Size>
+        <Anchors>
+            <Anchor point="CENTER" x="0" y="0"/>
+        </Anchors>
+        <Frames>
+            <!-- Page definitions - Configuration List -->
+            <Frame name="$parentPage1" parentKey="page1" hidden="true">
+                <Frames>
+                    <Button name="$parentColumn1" parentKey="column1" inherits="CliqueColumnTemplate">
+                        <Anchors>
+                            <Anchor point="TOPLEFT">
+                                <Offset x="0" y="0"/>
+                            </Anchor>
+                        </Anchors>
+                        <Scripts>
+                            <OnLoad>
+                                WhoFrameColumn_SetWidth(self, 203);
+                            </OnLoad>
+                        </Scripts>
+                    </Button>
+                    <Button name="$parentColumn2" parentKey="column2" inherits="CliqueColumnTemplate">
                         <Anchors>
-                                <Anchor point="CENTER">
+                            <Anchor point="TOPLEFT" relativePoint="TOPRIGHT" relativeTo="$parentColumn1">
+                                <Offset x="-2" y="0"/>
+                            </Anchor>
+                        </Anchors>
+                        <Scripts>
+                            <OnLoad>
+                                WhoFrameColumn_SetWidth(self, 120);
+                            </OnLoad>
+                        </Scripts>
+                        <ButtonText>
+                            <Anchors>
+                                <Anchor point="RIGHT">
+                                    <Offset>
+                                        <AbsDimension x="-8" y="0"/>
+                                    </Offset>
+                                </Anchor>
+                            </Anchors>
+                        </ButtonText>
+                    </Button>
+                    <Slider name="$parent_VSlider" parentKey="slider" valueStep="1.0" hidden="true" orientation="VERTICAL">
+                        <Size x="18" y="200"/>
+                        <Anchors>
+                            <Anchor point="TOPRIGHT">
+                                <Offset x="-44" y="-90"/>
+                            </Anchor>
+                            <Anchor point="BOTTOMRIGHT">
+                                <Offset x="0" y="83"/>
+                            </Anchor>
+                        </Anchors>
+                        <Backdrop edgeFile="Interface\Buttons\UI-SliderBar-Border"
+                            bgFile="Interface\Buttons\UI-SliderBar-Background"
+                            tile="true">
+                            <EdgeSize>
+                                <AbsValue val="8"/>
+                            </EdgeSize>
+                            <TileSize>
+                                <AbsValue val="8"/>
+                            </TileSize>
+                            <BackgroundInsets>
+                                <AbsInset left="3" right="3" top="6" bottom="6"/>
+                            </BackgroundInsets>
+                        </Backdrop>
+                        <Scripts>
+                            <OnValueChanged>
+                                CliqueConfig:UpdateList()
+                            </OnValueChanged>
+                        </Scripts>
+                        <ThumbTexture name="$parentThumbTexture"
+                            file="Interface\Buttons\UI-ScrollBar-Knob">
+                            <Size x="24" y="24"/>
+                        </ThumbTexture>
+                    </Slider>
+                </Frames>
+                <Scripts>
+                    <OnShow>
+                        CliqueConfig:EnableSpellbookButtons()
+                    </OnShow>
+                    <OnHide>
+                        CliqueConfig:EnableSpellbookButtons()
+                    </OnHide>
+                </Scripts>
+            </Frame> <!-- Page 1 - Configuration List -->
+
+            <!-- Page definitions - Edit Page -->
+
+            <Frame name="$parentPage2" parentKey="page2" hidden="true">
+                <Frames>
+                    <!-- Wrap the scroll frame in a frame that can capture clicks -->
+                    <Button name="CliqueClickGrabber" parentKey="clickGrabber">
+                        <Size x="320" y="160" />
+                        <Anchors>
+                            <Anchor point="BOTTOMLEFT" relativeTo="$parent" relativePoint="BOTTOMLEFT">
+                                <Offset>
+                                    <AbsDimension x="20" y="85"/>
+                                </Offset>
+                            </Anchor>
+                        </Anchors>
+                        <Backdrop bgFile="Interface\Tooltips\UI-Tooltip-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
+                            <EdgeSize>
+                                <AbsValue val="16"/>
+                            </EdgeSize>
+                            <TileSize>
+                                <AbsValue val="16"/>
+                            </TileSize>
+                            <BackgroundInsets>
+                                <AbsInset left="5" right="5" top="5" bottom="5"/>
+                            </BackgroundInsets>
+                        </Backdrop>
+                        <Scripts>
+                            <OnLoad>
+                                self:SetBackdropColor(0.3, 0.3, 0.3)
+                                self:SetBackdropBorderColor(0.5, 0.5, 0.5)
+                            </OnLoad>
+                            <OnClick>
+                                CliqueScrollFrameEditBox:SetFocus()
+                            </OnClick>
+                        </Scripts>
+                        <Frames>
+                            <ScrollFrame name="CliqueScrollFrame" parentKey="scrollFrame" inherits="UIPanelScrollFrameTemplate">
+                                <Size>
+                                    <AbsDimension x="288" y="150"/>
+                                </Size>
+                                <Anchors>
+                                    <Anchor point="TOPLEFT" relativeTo="$parent" relativePoint="TOPLEFT">
                                         <Offset>
-                                                <AbsDimension x="0" y="-1"/>
+                                            <AbsDimension x="5" y="-5"/>
                                         </Offset>
-                                </Anchor>
+                                    </Anchor>
+                                </Anchors>
+                                <ScrollChild>
+                                    <EditBox name="$parentEditBox" multiLine="true" autoFocus="false" countInvisibleLetters="true">
+                                        <Size>
+                                            <AbsDimension x="288" y="85"/>
+                                        </Size>
+                                        <Scripts>
+                                            <OnTextChanged>
+                                                ScrollingEdit_OnTextChanged(self, self:GetParent());
+                                            </OnTextChanged>
+                                            <OnCursorChanged function="ScrollingEdit_OnCursorChanged"/>
+                                            <OnUpdate>
+                                                ScrollingEdit_OnUpdate(self, elapsed, self:GetParent());
+                                            </OnUpdate>
+                                            <OnEscapePressed function="EditBox_ClearFocus"/>
+                                        </Scripts>
+                                        <FontString inherits="GameFontHighlightSmall"/>
+                                    </EditBox>
+                                </ScrollChild>
+                            </ScrollFrame>
+                        </Frames>
+                    </Button>
+                    <Button name="CliqueConfigSave" parentKey="save" inherits="UIPanelButtonTemplate2" text="SAVE">
+                        <Size x="82" y="22"/>
+                        <Anchors>
+                            <Anchor point="BOTTOMRIGHT">
+                                <Offset x="-40" y="64"/>
+                            </Anchor>
+                        </Anchors>
+                        <Scripts>
+                            <OnClick>CliqueConfig:Save_OnClick(self, button, down)</OnClick>
+                        </Scripts>
+                    </Button>
+                    <Button name="CliqueConfigCancel" parentKey="cancel" inherits="UIPanelButtonTemplate2" text="CANCEL">
+                        <Size x="82" y="22"/>
+                        <Anchors>
+                            <Anchor point="RIGHT" relativeTo="CliqueConfigSave" relativePoint="LEFT">
+                                <Offset x="2" y="0"/>
+                            </Anchor>
                         </Anchors>
-                </NormalTexture>
-                <HighlightTexture alphaMode="ADD" file="Interface\Buttons\ButtonHilight-Square"/>
-                <CheckedTexture alphaMode="ADD" file="Interface\Buttons\CheckButtonHilight"/>
+                        <Scripts>
+                            <OnClick>CliqueConfig:Cancel_OnClick(self, button, down)</OnClick>
+                        </Scripts>
+                    </Button>
+                </Frames>
+            </Frame> <!-- Page 2 - Edit Page -->
+        </Frames>
         <Scripts>
-            <OnClick>
-                Clique:SetSpellIcon(self)
-            </OnClick>
+            <OnLoad>
+                CliqueConfigPortrait:SetTexture("Interface\\FriendsFrame\\FriendsFrameScrollIcon");
+                CliqueConfig.page1:SetParent(CliqueConfig.Inset)
+                CliqueConfig.page2:SetParent(CliqueConfig.Inset)
+                CliqueConfig.page1:SetAllPoints(CliqueConfig.Inset)
+                CliqueConfig.page2:SetAllPoints(CliqueConfig.Inset)
+            </OnLoad>
+            <OnShow>
+                CliqueConfig:OnShow()
+            </OnShow>
         </Scripts>
-        </CheckButton>
-
-	<Frame name="CliqueEditTemplate" virtual="true" hidden="true">
-		  <Size>
-			<AbsDimension x="286" y="106"/>
-		  </Size>
-			<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\Tooltips\UI-Tooltip-Border" tile="true">
-			  <BackgroundInsets>
-				<AbsInset left="4" right="4" top="4" bottom="4" />
-			  </BackgroundInsets>
-			  <TileSize>
-				<AbsValue val="16" />
-			  </TileSize>
-			  <EdgeSize>
-				<AbsValue val="16" />
-			  </EdgeSize>
-			</Backdrop>
-		<Frames>
-		  <ScrollFrame name="$parentScrollFrame" inherits="UIPanelScrollFrameTemplate">
-			<Anchors>
-				<Anchor point="TOPLEFT">
-					<Offset><AbsDimension x="8" y="-8"/></Offset>
-				</Anchor>
-				<Anchor point="BOTTOMRIGHT">
-					<Offset><AbsDimension x="-8" y="8"/></Offset>
-				</Anchor>
-			</Anchors>
-			<ScrollChild>
-			  <EditBox name="$parentEditBox" multiLine="true" letters="255" autoFocus="false">
-				<Size>
-				  <AbsDimension x="270" y="90"/>
-				</Size>
-				<Scripts>
-				  <OnTextChanged>
-					local scrollBar = getglobal(this:GetParent():GetName().."ScrollBar")
-					this:GetParent():UpdateScrollChildRect();
-					local min;
-					local max;
-					min, max = scrollBar:GetMinMaxValues();
-					if ( max > 0 and (this.max ~= max) ) then
-					this.max = max;
-					scrollBar:SetValue(max);
-					end
-				  </OnTextChanged>
-				  <OnEscapePressed>
-					this:ClearFocus();
-				  </OnEscapePressed>
-				</Scripts>
-				<FontString inherits="GameFontHighlightSmall"/>
-			  </EditBox>
-			</ScrollChild>
-		  </ScrollFrame>
-		</Frames>
-	</Frame>
+    </Frame> <!-- CliqueConfig -->
 </Ui>
+
diff --git a/CliqueOptions.lua b/CliqueOptions.lua
old mode 100644
new mode 100755
index 001f86a..14dd4d6
--- a/CliqueOptions.lua
+++ b/CliqueOptions.lua
@@ -1,1986 +1,247 @@
-local genv = getfenv(0)
-local Clique = genv.Clique
-local L = Clique.Locals
-local StaticPopupDialogs = genv.StaticPopupDialogs
-local TEXT = genv.TEXT
-local OKAY = genv.OKAY
-local CANCEL = genv.CANCEL
-local GameTooltip = genv.GameTooltip
+local addonName, addon = ...
+local L = addon.L

-local NUM_ENTRIES = 10
-local ENTRY_SIZE = 35
-local work = {}
+local MAX_ROWS = 19

-function Clique:OptionsOnLoad()
-    -- Create a set of buttons to hook the SpellbookFrame
-    self.spellbuttons = {}
-    local onclick = function(frame, button) Clique:SpellBookButtonPressed(frame, button) end
-    local onleave = function(button)
-        button.updateTooltip = nil
-        GameTooltip:Hide()
+function CliqueConfig:OnShow()
+    if not self.initialized then
+        self:SetupGUI()
+        self:HijackSpellbook()
+        self.initialized = true
     end

-    for i=1,12 do
-        local parent = getglobal("SpellButton"..i)
-        local button = CreateFrame("Button", "SpellButtonCliqueCover"..i, parent)
-        button:SetID(parent:GetID())
-        button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
-        button:RegisterForClicks("AnyUp")
-        button:SetAllPoints(parent)
-        button:SetScript("OnClick", onclick)
-        button:SetScript("OnEnter", function(self)
-			local parent = self:GetParent()
-            if parent:IsEnabled() == 1 then
-                SpellButton_OnEnter(parent)
-            else
-                button:GetHighlightTexture():Hide()
-            end
-		end)
-        button:SetScript("OnLeave", onleave)
-
-		button:Hide()
-        self.spellbuttons[i] = button
-    end
-
-    CreateFrame("CheckButton", "CliquePulloutTab", SpellButton1, "SpellBookSkillLineTabTemplate")
-    CliquePulloutTab:SetNormalTexture("Interface\\AddOns\\Clique\\Images\\CliqueIcon")
-    CliquePulloutTab:SetScript("OnClick", function() Clique:Toggle() end)
-    CliquePulloutTab:SetScript("OnEnter", function() local i = 1 end)
-    CliquePulloutTab:SetScript("OnShow", function()
-		Clique.inuse = nil
-        for k,v in pairs(self.clicksets) do
-            if next(v) then
-                Clique.inuse = true
-            end
-        end
-        if not Clique.inuse then
-            CliqueFlashFrame.texture:Show()
-            CliqueFlashFrame.texture:SetAlpha(1.0)
-
-            local counter, loops, fading = 0, 0, true
-            CliqueFlashFrame:SetScript("OnUpdate", function(self, elapsed)
-                counter = counter + elapsed
-                if counter > 0.5 then
-                    loops = loops + 0.5
-                    fading = not fading
-                    counter = counter - 0.5
-                end
-
-                if loops > 30 then
-                    self.texture:Hide()
-                    self:SetScript("OnUpdate", nil)
-                    return
-                end
-
-                local texture = self.texture
-                if fading then
-                    texture:SetAlpha(1.0 - (counter / 0.5))
-                else
-                    texture:SetAlpha(counter / 0.5)
-                end
-            end)
-        end
-    end)
-	CliquePulloutTab:SetScript("OnEnter", function(self)
-		GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
-		GameTooltip:SetText("Clique configuration")
-		GameTooltip:Show()
-	end)
-
-    local frame = CreateFrame("Frame", "CliqueFlashFrame", CliquePulloutTab)
-    frame:SetWidth(10) frame:SetHeight(10)
-    frame:SetPoint("CENTER", 0, 0)
-
-    local texture = frame:CreateTexture(nil, "OVERLAY")
-    texture:SetTexture("Interface\\Buttons\\CheckButtonGlow")
-    texture:SetHeight(64) texture:SetWidth(64)
-    texture:SetPoint("CENTER", 0, 0)
-    texture:Hide()
-    CliqueFlashFrame.texture = texture
-
-    CliquePulloutTab:Show()
-
-	-- Hook the container buttons
-	local containerFunc = function(button)
-		if IsShiftKeyDown() and CliqueCustomArg1 then
-			if CliqueCustomArg1:HasFocus() then
-				CliqueCustomArg1:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID()))
-			elseif CliqueCustomArg2:HasFocus() then
-				CliqueCustomArg2:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID()))
-			elseif CliqueCustomArg3:HasFocus() then
-				CliqueCustomArg3:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID()))
-			elseif CliqueCustomArg4:HasFocus() then
-				CliqueCustomArg4:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID()))
-			elseif CliqueCustomArg5:HasFocus() then
-				CliqueCustomArg5:Insert(GetContainerItemLink(button:GetParent():GetID(), button:GetID()))
-			end
-		end
-	end
-
-	hooksecurefunc("ContainerFrameItemButton_OnModifiedClick", containerFunc)
-
-	-- Hook the bank buttons
-	local bankFunc = function(button)
-		if IsShiftKeyDown() and CliqueCustomArg1 then
-			if CliqueCustomArg1:HasFocus() then
-				CliqueCustomArg1:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID()))
-			elseif CliqueCustomArg2:HasFocus() then
-				CliqueCustomArg2:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID()))
-			elseif CliqueCustomArg3:HasFocus() then
-				CliqueCustomArg3:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID()))
-			elseif CliqueCustomArg4:HasFocus() then
-				CliqueCustomArg4:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID()))
-			elseif CliqueCustomArg5:HasFocus() then
-				CliqueCustomArg5:Insert(GetContainerItemLink(BANK_CONTAINER, button:GetID()))
-			end
-		end
-	end
-
-	hooksecurefunc("BankFrameItemButtonGeneric_OnModifiedClick", bankFunc)
-
-	-- Hook the paper doll frame buttons
-	local dollFunc = function(button)
-		if IsShiftKeyDown() and CliqueCustomArg1 then
-			if CliqueCustomArg1:HasFocus() then
-				CliqueCustomArg1:Insert(GetInventoryItemLink("player", button:GetID()))
-			elseif CliqueCustomArg2:HasFocus() then
-				CliqueCustomArg2:Insert(GetInventoryItemLink("player", button:GetID()))
-			elseif CliqueCustomArg3:HasFocus() then
-				CliqueCustomArg3:Insert(GetInventoryItemLink("player", button:GetID()))
-			elseif CliqueCustomArg4:HasFocus() then
-				CliqueCustomArg4:Insert(GetInventoryItemLink("player", button:GetID()))
-			elseif CliqueCustomArg5:HasFocus() then
-				CliqueCustomArg5:Insert(GetInventoryItemLink("player", button:GetID()))
-			end
-		end
-	end
-	hooksecurefunc("PaperDollItemSlotButton_OnModifiedClick", dollFunc)
-end
-
-function Clique:LEARNED_SPELL_IN_TAB()
-    local num = GetNumSpellTabs()
-    CliquePulloutTab:ClearAllPoints()
-    CliquePulloutTab:SetPoint("TOPLEFT","SpellBookSkillLineTab"..(num),"BOTTOMLEFT",0,-17)
-end
-
-function Clique:ToggleSpellBookButtons()
-   local method = CliqueFrame:IsVisible() and "Show" or "Hide"
-   local buttons = self.spellbuttons
-   for i=1,12 do
-      buttons[i][method](buttons[i])
-   end
-end
-
-function Clique:Toggle()
-    if not CliqueFrame then
-        Clique:CreateOptionsFrame()
-		CliqueFrame:Hide()
-		CliqueFrame:Show()
-	else
-        if CliqueFrame:IsVisible() then
-            CliqueFrame:Hide()
-			CliquePulloutTab:SetChecked(nil)
-        else
-            CliqueFrame:Show()
-			CliquePulloutTab:SetChecked(true)
-        end
-    end
-
-    Clique:ToggleSpellBookButtons()
-    self:ListScrollUpdate()
+    self:UpdateList()
+    self:EnableSpellbookButtons()
 end

--- This code is contributed with permission from Beladona
-local ondragstart = function(self)
-	self:GetParent():StartMoving()
-end
-
-local ondragstop = function(self)
-	self:GetParent():StopMovingOrSizing()
-	self:GetParent():SetUserPlaced()
-end
-
-function Clique:SkinFrame(frame)
-	frame:SetBackdrop({
-		bgFile = "Interface\\AddOns\\Clique\\images\\backdrop.tga",
-		edgeFile = "Interface\\AddOns\\Clique\\images\\borders.tga", tile = true,
-		tileSize = 32, edgeSize = 16,
-		insets = {left = 16, right = 16, top = 16, bottom = 16}
-	});
-
-	frame:EnableMouse()
-	frame:SetClampedToScreen(true)
-
-	frame.titleBar = CreateFrame("Button", nil, frame)
-	frame.titleBar:SetHeight(32)
-	frame.titleBar:SetPoint("TOPLEFT", frame, "TOPLEFT", 2, -2)
-	frame.titleBar:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -2, -2)
-	frame:SetMovable(true)
-	frame:SetFrameStrata("MEDIUM")
-	frame.titleBar:RegisterForDrag("LeftButton")
-	frame.titleBar:SetScript("OnDragStart", ondragstart)
-	frame.titleBar:SetScript("OnDragStop", ondragstop)
-
-	frame.headerLeft = frame.titleBar:CreateTexture(nil, "ARTWORK");
-	frame.headerLeft:SetTexture("Interface\\AddOns\\Clique\\images\\headCorner.tga");
-	frame.headerLeft:SetWidth(32); frame.headerLeft:SetHeight(32);
-	frame.headerLeft:SetPoint("TOPLEFT", 0, 0);
-
-	frame.headerRight = frame.titleBar:CreateTexture(nil, "ARTWORK");
-	frame.headerRight:SetTexture("Interface\\AddOns\\Clique\\images\\headCorner.tga");
-	frame.headerRight:SetTexCoord(1,0,0,1);
-	frame.headerRight:SetWidth(32); frame.headerRight:SetHeight(32);
-	frame.headerRight:SetPoint("TOPRIGHT", 0, 0);

-	frame.header = frame.titleBar:CreateTexture(nil, "ARTWORK");
-	frame.header:SetTexture("Interface\\AddOns\\Clique\\images\\header.tga");
-	frame.header:SetPoint("TOPLEFT", frame.headerLeft, "TOPRIGHT");
-	frame.header:SetPoint("BOTTOMRIGHT", frame.headerRight, "BOTTOMLEFT");
-
-	frame.title = frame.titleBar:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall");
-	frame.title:SetWidth(200); frame.title:SetHeight(16);
-	frame.title:SetPoint("TOP", 0, -2);
-
-	frame.footerLeft = frame:CreateTexture(nil, "ARTWORK");
-	frame.footerLeft:SetTexture("Interface\\AddOns\\Clique\\images\\footCorner.tga");
-	frame.footerLeft:SetWidth(48); frame.footerLeft:SetHeight(48);
-	frame.footerLeft:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 2, 2);
-
-	frame.footerRight = frame:CreateTexture(nil, "ARTWORK");
-	frame.footerRight:SetTexture("Interface\\AddOns\\Clique\\images\\footCorner.tga");
-	frame.footerRight:SetTexCoord(1,0,0,1);
-	frame.footerRight:SetWidth(48); frame.footerRight:SetHeight(48);
-	frame.footerRight:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -2, 2);
-
-	frame.footer = frame:CreateTexture(nil, "ARTWORK");
-	frame.footer:SetTexture("Interface\\AddOns\\Clique\\images\\footer.tga");
-	frame.footer:SetPoint("TOPLEFT", frame.footerLeft, "TOPRIGHT");
-	frame.footer:SetPoint("BOTTOMRIGHT", frame.footerRight, "BOTTOMLEFT");
-end
-
-function Clique:CreateOptionsFrame()
-    local frames = {}
-    self.frames = frames
-
-    local frame = CreateFrame("Frame", "CliqueFrame", CliquePulloutTab)
-    frame:SetHeight(415)
-    frame:SetWidth(400)
-    frame:SetPoint("LEFT", SpellBookFrame, "RIGHT", 15, 30)
-	self:SkinFrame(frame)
-	frame:SetToplevel(true)
-	frame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile));
-	frame:SetScript("OnShow", function()
-        frame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile));
-		if Clique.inuse then
-			CliqueHelpText:Hide()
-		else
-			CliqueHelpText:Show()
-		end
-	end)
-
-	CliqueFrame:SetScript("OnShow", function(self)
-		if InCombatLockdown() then
-			Clique:Toggle()
-			return
-		end
-		local parent = self:GetParent()
-		self:SetFrameLevel(parent:GetFrameLevel() + 5)
-		Clique:ToggleSpellBookButtons()
-	end)
-
-	CliqueFrame:SetScript("OnHide", function() Clique:ToggleSpellBookButtons() end)
-	CliqueFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
-	CliqueFrame:SetScript("OnEvent", function(self, event, ...)
-		if self:IsVisible() then
-			Clique:Toggle()
-		end
-	end)
-
-    local frame = CreateFrame("Frame", "CliqueListFrame", CliqueFrame)
-    frame:SetAllPoints()
-
-    local onclick = function(button)
-		local offset = FauxScrollFrame_GetOffset(CliqueListScroll)
-		self.listSelected = offset + button.id
-		Clique:ListScrollUpdate()
-    end
-
-	local ondoubleclick = function(button)
-		onclick(button)
-		CliqueButtonEdit:Click()
-	end
-
-    local onenter = function(button) button:SetBackdropBorderColor(1, 1, 1) end
-    local onleave = function(button)
-        local selected = FauxScrollFrame_GetOffset(CliqueListScroll) + button.id
-        if selected == self.listSelected then
-            button:SetBackdropBorderColor(1, 1, 0)
-        else
-            button:SetBackdropBorderColor(0.3, 0.3, 0.3)
-        end
+function CliqueConfig:SetupGUI()
+    self.rows = {}
+    for i = 1, MAX_ROWS do
+        self.rows[i] = CreateFrame("Button", "CliqueRow" .. i, CliqueConfig, "CliqueRowTemplate")
     end

-    for i=1,NUM_ENTRIES do
-        local entry = CreateFrame("Button", "CliqueList"..i, frame)
-        entry.id = i
-        entry:SetHeight(ENTRY_SIZE)
-        entry:SetWidth(390)
-        entry:SetBackdrop({
-          bgFile="Interface\\Tooltips\\UI-Tooltip-Background",
-          edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
-          tile = true, tileSize = 8, edgeSize = 16,
-          insets = {left = 2, right = 2, top = 2, bottom = 2}})
+    self.rows[1]:ClearAllPoints()
+    self.rows[1]:SetPoint("TOPLEFT", "CliqueConfigPage1Column1", "BOTTOMLEFT", 0, -6)

-        entry:SetBackdropBorderColor(0.3, 0.3, 0.3)
-        entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3)
-        entry:SetScript("OnClick", onclick)
-        entry:SetScript("OnEnter", onenter)
-        entry:SetScript("OnLeave", onleave)
-		entry:SetScript("OnDoubleClick", ondoubleclick)
-
-        entry.icon = entry:CreateTexture(nil, "ARTWORK")
-        entry.icon:SetHeight(24)
-        entry.icon:SetWidth(24)
-        entry.icon:SetPoint("LEFT", 5, 0)
-
-        entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-        entry.name:SetPoint("LEFT", entry.icon, "RIGHT", 5, 0)
-
-        entry.binding = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
-        entry.binding:SetPoint("RIGHT", entry, "RIGHT", -5, 0)
-        frames[i] = entry
-    end
-
-    frames[1]:SetPoint("TOPLEFT", 5, -55)
-    for i=2,NUM_ENTRIES do
-        frames[i]:SetPoint("TOP", frames[i-1], "BOTTOM", 0, 2)
+    for i = 2, MAX_ROWS do
+        self.rows[i]:ClearAllPoints()
+        self.rows[i]:SetPoint("TOPLEFT", self.rows[i - 1], "BOTTOMLEFT")
     end
-
-    local endButton = getglobal("CliqueList"..NUM_ENTRIES)
-    CreateFrame("ScrollFrame", "CliqueListScroll", CliqueListFrame, "FauxScrollFrameTemplate")
-    CliqueListScroll:SetPoint("TOPLEFT", CliqueList1, "TOPLEFT", 0, 0)
-    CliqueListScroll:SetPoint("BOTTOMRIGHT", endButton, "BOTTOMRIGHT", 0, 0)
-
-    local texture = CliqueListScroll:CreateTexture(nil, "BACKGROUND")
-    texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-    texture:SetPoint("TOPLEFT", CliqueListScroll, "TOPRIGHT", 14, 0)
-    texture:SetPoint("BOTTOMRIGHT", CliqueListScroll, "BOTTOMRIGHT", 23, 0)
-    texture:SetGradientAlpha("HORIZONTAL", 0.5, 0.25, 0.05, 0, 0.15, 0.15, 0.15, 1)
-
-    local texture = CliqueListScroll:CreateTexture(nil, "BACKGROUND")
-    texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-    texture:SetPoint("TOPLEFT", CliqueListScroll, "TOPRIGHT", 4, 0)
-    texture:SetPoint("BOTTOMRIGHT", CliqueListScroll, "BOTTOMRIGHT", 14, 0)
-    texture:SetGradientAlpha("HORIZONTAL", 0.15, 0.15, 0.15, 0.15, 1, 0.5, 0.25, 0.05, 0)
-
-    local update = function() Clique:ListScrollUpdate() end

-	CliqueListScroll:SetScript("OnVerticalScroll", update, function(self, offset)
-		FauxScrollFrame_OnVerticalScroll(self, offset, ENTRY_SIZE, update)
-	end)
+    -- Set text elements using localized values
+    _G[self:GetName() .. "TitleText"]:SetText(L["Clique Configuration"])
+    self.page1.column1:SetText(L["Action"])
+    self.page1.column2:SetText(L["Binding"])

-    CliqueListScroll:SetScript("OnShow", update)
-
-    local frame = CreateFrame("Frame", "CliqueTextListFrame", CliqueFrame)
-    frame:SetHeight(300)
-    frame:SetWidth(250)
-    frame:SetPoint("BOTTOMLEFT", CliqueFrame, "BOTTOMRIGHT", 0, 0)
-	self:SkinFrame(frame)
-	frame:SetFrameStrata("HIGH")
+    self.page1:Show()
+end

-	frame:SetScript("OnShow", function(self)
-		local parent = self:GetParent()
-		self:SetFrameLevel(parent:GetFrameLevel() + 5)
-	end)
+function CliqueConfig:HijackSpellbook()
+    self.spellbookButtons = {}

-    local onclick = function(button)
-	    local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll)
-		if self.textlistSelected == offset + button.id then
-			self.textlistSelected = nil
-		else
-			self.textlistSelected = offset + button.id
-		end
-		if self.textlist == "FRAMES" then
-			local name = button.name:GetText()
-			local frame = getglobal(name)
-			if button:GetChecked() then
-				self.profile.blacklist[name] = nil
-				self:RegisterFrame(getglobal(name))
-			else
-				self:UnregisterFrame(frame)
-				self.profile.blacklist[name] = true
-			end
-		end
-        Clique:TextListScrollUpdate()
-    end
-
-    local onenter = function(button) button:SetBackdropBorderColor(1, 1, 1) end
-    local onleave = function(button)
-        local selected = FauxScrollFrame_GetOffset(CliqueTextListScroll) + button.id
-		button:SetBackdropBorderColor(0.3, 0.3, 0.3)
+    for idx = 1, 12 do
+        local parent = getglobal("SpellButton" .. idx)
+        local button = CreateFrame("Button", "CliqueSpellbookButton" .. idx, parent, "CliqueSpellbookButtonTemplate")
+        button.spellbutton = parent
+        button:EnableKeyboard(false)
+        button:EnableMouseWheel(true)
+        button:RegisterForClicks("AnyDown")
+        button:SetID(parent:GetID())
+        self.spellbookButtons[idx] = button
     end

-	local frames = {}
-
-	for i=1,12 do
-		local entry = CreateFrame("CheckButton", "CliqueTextList"..i, frame)
-		entry.id = i
-		entry:SetHeight(22)
-		entry:SetWidth(240)
-        entry:SetBackdrop({
---          bgFile="Interface\\Tooltips\\UI-Tooltip-Background",
---          edgeFile = "Interface/Tooltips/UI-Tooltip-Border",
---          tile = true, tileSize = 8, edgeSize = 16,
-          insets = {left = 2, right = 2, top = 2, bottom = 2}})
-
-        entry:SetBackdropBorderColor(0.3, 0.3, 0.3)
-        entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3)
-        entry:SetScript("OnClick", onclick)
-        entry:SetScript("OnEnter", onenter)
-        entry:SetScript("OnLeave", onleave)
-
-		local texture = entry:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		entry:SetNormalTexture(texture)
-
-		local texture = entry:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		texture:SetBlendMode("ADD")
-		entry:SetHighlightTexture(texture)
-
-		local texture = entry:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		entry:SetCheckedTexture(texture)
-
-		entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-		entry.name:SetPoint("LEFT", 25, 0)
-		entry.name:SetJustifyH("LEFT")
-		entry.name:SetText("Profile Name")
-		frames[i] = entry
-	end
-
-	frames[1]:SetPoint("TOPLEFT", 5, -25)
-	for i=2,12 do
-		frames[i]:SetPoint("TOP", frames[i-1], "BOTTOM", 0, 2)
-	end
-
-    local endButton = CliqueTextList12
-    CreateFrame("ScrollFrame", "CliqueTextListScroll", CliqueTextListFrame, "FauxScrollFrameTemplate")
-    CliqueTextListScroll:SetPoint("TOPLEFT", CliqueTextList1, "TOPLEFT", 0, 0)
-    CliqueTextListScroll:SetPoint("BOTTOMRIGHT", endButton, "BOTTOMRIGHT", 0, 0)
-
-    local texture = CliqueTextListScroll:CreateTexture(nil, "BACKGROUND")
-    texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-    texture:SetPoint("TOPLEFT", CliqueTextListScroll, "TOPRIGHT", 14, 0)
-    texture:SetPoint("BOTTOMRIGHT", CliqueTextListScroll, "BOTTOMRIGHT", 23, 0)
-    texture:SetGradientAlpha("HORIZONTAL", 0.5, 0.25, 0.05, 0, 0.15, 0.15, 0.15, 1)
-
-    local texture = CliqueTextListScroll:CreateTexture(nil, "BACKGROUND")
-    texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-    texture:SetPoint("TOPLEFT", CliqueTextListScroll, "TOPRIGHT", 4, 0)
-    texture:SetPoint("BOTTOMRIGHT", CliqueTextListScroll, "BOTTOMRIGHT", 14, 0)
-    texture:SetGradientAlpha("HORIZONTAL", 0.15, 0.15, 0.15, 0.15, 1, 0.5, 0.25, 0.05, 0)
-
-    local update = function()
-		Clique:TextListScrollUpdate()
-	end
-
-    CliqueTextListScroll:SetScript("OnVerticalScroll", function(self, offset)
-		FauxScrollFrame_OnVerticalScroll(self, offset, 22, update)
-	end)
-    CliqueTextListFrame:SetScript("OnShow", update)
-	CliqueTextListFrame:Hide()
-
-	-- Dropdown Frame
-	CreateFrame("Frame", "CliqueDropDown", CliqueFrame, "UIDropDownMenuTemplate")
-	CliqueDropDown:SetID(1)
-	CliqueDropDown:SetPoint("TOPRIGHT", -115, -25)
-	CliqueDropDown:SetScript("OnShow", function(self) Clique:DropDown_OnShow(self) end)
-
-	CliqueDropDownButton:SetScript("OnEnter", function(self)
-		GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
-		GameTooltip:SetText("Select a click-set to edit")
-		GameTooltip:Show()
-	end)
-	CliqueDropDownButton:SetScript("OnLeave", function(self)
-		GameTooltip:Hide()
-	end)
-
-	local font = CliqueDropDown:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
-	font:SetText("Click Set:")
-	font:SetPoint("RIGHT", CliqueDropDown, "LEFT", 5, 3)
-	-- Button Creations
-    local buttonFunc = function(self) Clique:ButtonOnClick(self) end
-
-	local button = CreateFrame("Button", "CliqueButtonClose", CliqueFrame.titleBar, "UIPanelCloseButton")
-	button:SetHeight(25)
-	button:SetWidth(25)
-	button:SetPoint("TOPRIGHT", -5, 3)
-	button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonCustom", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Custom")
-    button:SetPoint("BOTTOMLEFT", CliqueFrame, "BOTTOMLEFT", 10, 5)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonFrames", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Frames")
-    button:SetPoint("LEFT", CliqueButtonCustom, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonProfiles", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Profiles")
-    button:SetPoint("LEFT", CliqueButtonFrames, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonOptions", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Options")
-    button:SetPoint("LEFT", CliqueButtonProfiles, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonDelete", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Delete")
-    button:SetPoint("LEFT", CliqueButtonOptions, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonEdit", CliqueFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Edit")
-    button:SetPoint("LEFT", CliqueButtonDelete, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
- 	-- Buttons for text list scroll frame
-
-	local button = CreateFrame("Button", "CliqueTextButtonClose", CliqueTextListFrame.titleBar, "UIPanelCloseButton")
-	button:SetHeight(25)
-	button:SetWidth(25)
-	button:SetPoint("TOPRIGHT", -5, 3)
-	button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonDeleteProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Delete")
-    button:SetPoint("BOTTOMLEFT", CliqueTextListFrame, "BOTTOMLEFT", 30, 5)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonSetProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("Set")
-    button:SetPoint("LEFT", CliqueButtonDeleteProfile, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueButtonNewProfile", CliqueTextListFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(60)
-    button:SetText("New")
-    button:SetPoint("LEFT", CliqueButtonSetProfile, "RIGHT", 3, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-    local frame = CreateFrame("FramE", "CliqueOptionsFrame", CliqueFrame)
-    frame:SetHeight(200)
-    frame:SetWidth(300)
-    frame:SetPoint("CENTER", 0, 0)
-    self:SkinFrame(frame)
-    frame:SetFrameStrata("DIALOG")
-    frame.title:SetText(L["Clique Options"])
-    frame:Hide()
-    self:CreateOptionsWidgets(frame)
-
-	self.customEntry = {}
-    local frame = CreateFrame("Frame", "CliqueCustomFrame", CliqueFrame)
-    frame:SetHeight(400)
-	frame:SetWidth(450)
-	frame:SetPoint("CENTER", 70, -50)
-	self:SkinFrame(frame)
-	frame:SetFrameStrata("DIALOG")
-	frame.title:SetText("Clique Custom Editor");
-    frame:Hide()
-
-	frame:SetScript("OnShow", function(self)
-		local parent = self:GetParent()
-		self:SetFrameLevel(parent:GetFrameLevel() + 5)
-	end)
-
-	-- Help text for Custom screen
-
-	local font = frame:CreateFontString("CliqueCustomHelpText", "OVERLAY", "GameFontHighlight")
-	font:SetWidth(260) font:SetHeight(100)
-	font:SetPoint("TOPRIGHT", -10, -25)
-	font:SetText(L.CUSTOM_HELP)
-
-	local checkFunc = function(self) Clique:CustomRadio(self) end
-	self.radio = {}
-
-	local buttons = {
-		{type = "actionbar", name = L.ACTION_ACTIONBAR},
-		{type = "action", name = L.ACTION_ACTION},
-		{type = "pet", name = L.ACTION_PET},
-		{type = "spell", name = L.ACTION_SPELL},
-		{type = "item", name = L.ACTION_ITEM},
-		{type = "macro", name = L.ACTION_MACRO},
-		{type = "stop", name = L.ACTION_STOP},
-		{type = "target", name = L.ACTION_TARGET},
-		{type = "focus", name = L.ACTION_FOCUS},
-		{type = "assist", name = L.ACTION_ASSIST},
-		{type = "click", name = L.ACTION_CLICK},
-		{type = "menu", name = L.ACTION_MENU},
-	}
-
-	for i=1,#buttons do
-		local entry = buttons[i]
-
-		local name = "CliqueRadioButton"..entry.type
-		local button = CreateFrame("CheckButton", name, CliqueCustomFrame)
-		button:SetHeight(20)
-		button:SetWidth(150)
-
-		local texture = button:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioEmpty")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		button:SetNormalTexture(texture)
-
-		local texture = button:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioChecked")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		texture:SetBlendMode("ADD")
-		button:SetHighlightTexture(texture)
-
-		local texture = button:CreateTexture("ARTWORK")
-		texture:SetTexture("Interface\\AddOns\\Clique\\images\\RadioChecked")
-		texture:SetPoint("LEFT", 0, 0)
-		texture:SetHeight(26)
-		texture:SetWidth(26)
-		button:SetCheckedTexture(texture)
-
-		button.name = button:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-		button.name:SetPoint("LEFT", 25, 0)
-		button.name:SetJustifyH("LEFT")
-
-		local entry = buttons[1]
-		local name = "CliqueRadioButton"..entry.type
-		local button = CreateFrame("CheckButton", name, CliqueCustomFrame)
-		button:SetHeight(22)
-		button:SetWidth(150)
-	end
-
-	local entry = buttons[1]
-	local button = getglobal("CliqueRadioButton"..entry.type)
-	button.type = entry.type
-	button.name:SetText(entry.name)
-	button:SetPoint("TOPLEFT", 5, -30)
-	button:SetScript("OnClick", checkFunc)
-	self.radio[button] = true
-
-	local prev = button
-
-	for i=2,#buttons do
-		local entry = buttons[i]
-		local name = "CliqueRadioButton"..entry.type
-		local button = getglobal(name)
-
-		button.type = entry.type
-		button.name:SetText(entry.name)
-		button:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, 0)
-		button:SetScript("OnClick", checkFunc)
-		self.radio[button] = true
-		prev = button
-	end
-
-	-- Button to set the binding
-
-    local button = CreateFrame("Button", "CliqueCustomButtonBinding", CliqueCustomFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(30)
-    button:SetWidth(175)
-    button:SetText("Set Click Binding")
-    button:SetPoint("TOP", CliqueCustomHelpText, "BOTTOM", 40, -10)
-    button:SetScript("OnClick", function(self) Clique:CustomBinding_OnClick(self) end )
-	button:RegisterForClicks("AnyUp")
-
-	-- Button for icon selection
-
-	local button = CreateFrame("Button", "CliqueCustomButtonIcon", CliqueCustomFrame)
-	button.icon = button:CreateTexture(nil, "BORDER")
-	button.icon:SetAllPoints()
-	button.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
-	button:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square")
-	button:GetHighlightTexture():SetBlendMode("ADD")
-	button:SetHeight(30)
-	button:SetWidth(30)
-	button:SetPoint("RIGHT", CliqueCustomButtonBinding, "LEFT", -15, 0)
-
-    local func = function()
-		GameTooltip:SetOwner(button, "ANCHOR_TOPLEFT")
-		GameTooltip:SetText("Click here to set icon")
-		GameTooltip:Show()
+    local function showHideHandler(frame)
+        self:EnableSpellbookButtons()
     end
-
-    button:SetScript("OnEnter", func)
-    button:SetScript("OnLeave", function() GameTooltip:Hide() end)
-	button:SetScript("OnClick", function() CliqueIconSelectFrame:Show() end)
-
-	-- Create the editboxes for action arguments
-
-	local edit = CreateFrame("EditBox", "CliqueCustomArg1", CliqueCustomFrame, "InputBoxTemplate")
-	edit:SetHeight(30)
-	edit:SetWidth(200)
-	edit:SetPoint("TOPRIGHT", CliqueCustomFrame, "TOPRIGHT", -10, -190)
-	edit:SetAutoFocus(nil)
-	edit:SetScript("OnTabPressed", function()
-		if CliqueCustomArg2:IsVisible() then
-			CliqueCustomArg2:SetFocus()
-		end
-	end)
-	edit:SetScript("OnEnterPressed", function() end)
-
-	edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	edit.label:SetText("Spell Name:")
-	edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0)
-	edit.label:SetJustifyH("RIGHT")
-	edit:Hide()
-
-	-- Argument 2
-
-	local edit = CreateFrame("EditBox", "CliqueCustomArg2", CliqueCustomFrame, "InputBoxTemplate")
-	edit:SetHeight(30)
-	edit:SetWidth(200)
-	edit:SetPoint("TOPRIGHT", CliqueCustomArg1, "BOTTOMRIGHT", 0, 0)
-	edit:SetAutoFocus(nil)
-	edit:SetScript("OnTabPressed", function()
-		if CliqueCustomArg3:IsVisible() then
-			CliqueCustomArg3:SetFocus()
-		end
-	end)
-	edit:SetScript("OnEnterPressed", function() end)
-
-	edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	edit.label:SetText("Spell Name:")
-	edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0)
-	edit.label:SetJustifyH("RIGHT")
-	edit:Hide()
-
-	-- Multi line edit box
-
-	local edit = CreateFrame("ScrollFrame", "CliqueMulti", CliqueCustomFrame, "CliqueEditTemplate")
-	edit:SetPoint("TOPRIGHT", CliqueCustomArg1, "BOTTOMRIGHT", -10, -27)
-
-	local name = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	name:SetText("Macro Text:")
-	name:SetJustifyH("RIGHT")
-	name:SetPoint("RIGHT", CliqueCustomArg2.label)
-
-	local grabber = CreateFrame("Button", "CliqueFocusGrabber", edit)
-	grabber:SetPoint("TOPLEFT", 8, -8)
-	grabber:SetPoint("BOTTOMRIGHT", -8, 8)
-	grabber:SetScript("OnClick", function() CliqueMultiScrollFrameEditBox:SetFocus() end)
-
-	-- Argument 3
-
-	local edit = CreateFrame("EditBox", "CliqueCustomArg3", CliqueCustomFrame, "InputBoxTemplate")
-	edit:SetHeight(30)
-	edit:SetWidth(200)
-	edit:SetPoint("TOPRIGHT", CliqueCustomArg2, "BOTTOMRIGHT", 0, 0)
-	edit:SetAutoFocus(nil)
-	edit:SetScript("OnTabPressed", function()
-		if CliqueCustomArg4:IsVisible() then
-			CliqueCustomArg4:SetFocus()
-		end
-	end)
-	edit:SetScript("OnEnterPressed", function() end)
-
-	edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	edit.label:SetText("Spell Name:")
-	edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0)
-	edit.label:SetJustifyH("RIGHT")
-	edit:Hide()
-
-	-- Argument 4
-
-	local edit = CreateFrame("EditBox", "CliqueCustomArg4", CliqueCustomFrame, "InputBoxTemplate")
-	edit:SetHeight(30)
-	edit:SetWidth(200)
-	edit:SetPoint("TOPRIGHT", CliqueCustomArg3, "BOTTOMRIGHT", 0, 0)
-	edit:SetAutoFocus(nil)
-	edit:SetScript("OnTabPressed", function()
-		if CliqueCustomArg5:IsVisible() then
-			CliqueCustomArg5:SetFocus()
-		end
-	end)
-	edit:SetScript("OnEnterPressed", function() end)
-
-	edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	edit.label:SetText("Spell Name:")
-	edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0)
-	edit.label:SetJustifyH("RIGHT")
-	edit:Hide()
-
-	-- Argument 5
-
-	local edit = CreateFrame("EditBox", "CliqueCustomArg5", CliqueCustomFrame, "InputBoxTemplate")
-	edit:SetHeight(30)
-	edit:SetWidth(200)
-	edit:SetPoint("TOPRIGHT", CliqueCustomArg4, "BOTTOMRIGHT", 0, 0)
-	edit:SetAutoFocus(nil)
-	edit:SetScript("OnTabPressed", function()
-		if CliqueCustomArg1:IsVisible() then
-			CliqueCustomArg1:SetFocus()
-		end
-	end)
-	edit:SetScript("OnEnterPressed", function() end)
-
-	edit.label = edit:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-	edit.label:SetText("Spell Name:")
-	edit.label:SetPoint("RIGHT", edit, "LEFT", -10, 0)
-	edit.label:SetJustifyH("RIGHT")
-	edit:Hide()
-
-	-- Bottom buttons
-
-    local button = CreateFrame("Button", "CliqueCustomButtonCancel", CliqueCustomFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(70)
-    button:SetText("Cancel")
-    button:SetPoint("BOTTOM", 65, 4)
-    button:SetScript("OnClick", buttonFunc)
-
-    local button = CreateFrame("Button", "CliqueCustomButtonSave", CliqueCustomFrame, "UIPanelButtonGrayTemplate")
-    button:SetHeight(24)
-    button:SetWidth(70)
-    button:SetText("Save")
-    button:SetPoint("LEFT", CliqueCustomButtonCancel, "RIGHT", 6, 0)
-    button:SetScript("OnClick", buttonFunc)
-
-	-- Create the macro icon frame
-
-	CreateFrame("Frame", "CliqueIconSelectFrame", CliqueCustomFrame)
-	CliqueIconSelectFrame:SetWidth(296)
-	CliqueIconSelectFrame:SetHeight(250)
-	CliqueIconSelectFrame:SetPoint("CENTER",0,0)
-	self:SkinFrame(CliqueIconSelectFrame)
-	CliqueIconSelectFrame:SetFrameStrata("DIALOG")
-	CliqueIconSelectFrame.title:SetText("Select an icon")
-	CliqueIconSelectFrame:Hide()
-
-	CreateFrame("CheckButton", "CliqueIcon1", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon1:SetID(1)
-	CliqueIcon1:SetPoint("TOPLEFT", 25, -35)
-
-	CreateFrame("CheckButton", "CliqueIcon2", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon2:SetID(2)
-	CliqueIcon2:SetPoint("LEFT", CliqueIcon1, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon3", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon3:SetID(3)
-	CliqueIcon3:SetPoint("LEFT", CliqueIcon2, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon4", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon4:SetID(4)
-	CliqueIcon4:SetPoint("LEFT", CliqueIcon3, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon5", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon5:SetID(5)
-	CliqueIcon5:SetPoint("LEFT", CliqueIcon4, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon6", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon6:SetID(6)
-	CliqueIcon6:SetPoint("TOPLEFT", CliqueIcon1, "BOTTOMLEFT", 0, -10)
-
-	CreateFrame("CheckButton", "CliqueIcon7", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon7:SetID(7)
-	CliqueIcon7:SetPoint("LEFT", CliqueIcon6, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon8", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon8:SetID(8)
-	CliqueIcon8:SetPoint("LEFT", CliqueIcon7, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon9", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon9:SetID(9)
-	CliqueIcon9:SetPoint("LEFT", CliqueIcon8, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon10", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon10:SetID(10)
-	CliqueIcon10:SetPoint("LEFT", CliqueIcon9, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon11", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon11:SetID(11)
-	CliqueIcon11:SetPoint("TOPLEFT", CliqueIcon6, "BOTTOMLEFT", 0, -10)
-
-	CreateFrame("CheckButton", "CliqueIcon12", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon12:SetID(12)
-	CliqueIcon12:SetPoint("LEFT", CliqueIcon11, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon13", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon13:SetID(13)
-	CliqueIcon13:SetPoint("LEFT", CliqueIcon12, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon14", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon14:SetID(14)
-	CliqueIcon14:SetPoint("LEFT", CliqueIcon13, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon15", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon15:SetID(15)
-	CliqueIcon15:SetPoint("LEFT", CliqueIcon14, "RIGHT", 10, 0)
+    SpellBookFrame:HookScript("OnShow", showHideHandler)
+    SpellBookFrame:HookScript("OnHide", showHideHandler)

-	CreateFrame("CheckButton", "CliqueIcon16", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon16:SetID(16)
-	CliqueIcon16:SetPoint("TOPLEFT", CliqueIcon11, "BOTTOMLEFT", 0, -10)
-
-	CreateFrame("CheckButton", "CliqueIcon17", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon17:SetID(17)
-	CliqueIcon17:SetPoint("LEFT", CliqueIcon16, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon18", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon18:SetID(18)
-	CliqueIcon18:SetPoint("LEFT", CliqueIcon17, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon19", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon19:SetID(19)
-	CliqueIcon19:SetPoint("LEFT", CliqueIcon18, "RIGHT", 10, 0)
-
-	CreateFrame("CheckButton", "CliqueIcon20", CliqueIconSelectFrame, "CliqueIconTemplate")
-	CliqueIcon20:SetID(20)
-	CliqueIcon20:SetPoint("LEFT", CliqueIcon19, "RIGHT", 10, 0)
-
-	CreateFrame("ScrollFrame", "CliqueIconScrollFrame", CliqueIconSelectFrame, "FauxScrollFrameTemplate")
-	CliqueIconScrollFrame:SetPoint("TOPLEFT", CliqueIcon1, "TOPLEFT", 0, 0)
-	CliqueIconScrollFrame:SetPoint("BOTTOMRIGHT", CliqueIcon20, "BOTTOMRIGHT", 10, 0)
-
-	local texture = CliqueIconScrollFrame:CreateTexture(nil, "BACKGROUND")
-	texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-	texture:SetPoint("TOPLEFT", CliqueIconScrollFrame, "TOPRIGHT", 14, 0)
-	texture:SetPoint("BOTTOMRIGHT", 23, 0)
-	texture:SetVertexColor(0.3, 0.3, 0.3)
-
-	local texture = CliqueIconScrollFrame:CreateTexture(nil, "BACKGROUND")
-	texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
-	texture:SetPoint("TOPLEFT", CliqueIconScrollFrame, "TOPRIGHT", 4, 0)
-	texture:SetPoint("BOTTOMRIGHT", 14,0)
-	texture:SetVertexColor(0.3, 0.3, 0.3)
-
-	local function updateicons()
-		Clique:UpdateIconFrame()
-	end
-
-	CliqueIconScrollFrame:SetScript("OnVerticalScroll", function(self, offset)
-		local MACRO_ICON_ROW_HEIGHT = 36
-		FauxScrollFrame_OnVerticalScroll(self, offset, MACRO_ICON_ROW_HEIGHT, updateicons)
-	end)
-
-	CliqueIconSelectFrame:SetScript("OnShow", function(self)
-		local parent = self:GetParent()
-		self:SetFrameLevel(parent:GetFrameLevel() + 5)
-		Clique:UpdateIconFrame()
-	end)
-
-	-- Create the CliqueHelpText
-	CliqueFrame:CreateFontString("CliqueHelpText", "OVERLAY", "GameFontHighlight")
-	CliqueHelpText:SetText(L.HELP_TEXT)
-	CliqueHelpText:SetPoint("TOPLEFT", 10, -10)
-	CliqueHelpText:SetPoint("BOTTOMRIGHT", -10, 10)
-	CliqueHelpText:SetJustifyH("CENTER")
-	CliqueHelpText:SetJustifyV("CENTER")
-	CliqueHelpText:SetPoint("CENTER", 0, 0)
-
-    self.sortList = {}
-    self.listSelected = 0
+    -- TODO: This isn't a great way to do this, but for now
+    hooksecurefunc("SpellBookSkillLineTab_OnClick", showHideHandler)
+    self:EnableSpellbookButtons()
 end

-function Clique:ListScrollUpdate()
-	if not CliqueListScroll then return end
+function CliqueConfig:EnableSpellbookButtons()
+    local enabled;

-    local idx,button
-    Clique:SortList()
-    local clickCasts = self.sortList
-    local offset = FauxScrollFrame_GetOffset(CliqueListScroll)
-	FauxScrollFrame_Update(CliqueListScroll, table.getn(clickCasts), NUM_ENTRIES, ENTRY_SIZE)
-
-    if not CliqueListScroll:IsShown() then
-        CliqueFrame:SetWidth(400)
+    if self.page1:IsVisible() and SpellBookFrame:IsVisible() then
+        enabled = true
+        self:SetNotification("Your spellbook is open.  You can mouse over a spell in your spellbook and click or press a key conbination to add it to your bindings configuration")
     else
-        CliqueFrame:SetWidth(425)
+        method = false
+        self:ClearNotification()
     end
-
-    for i=1,NUM_ENTRIES do
-        idx = offset + i
-        button = getglobal("CliqueList"..i)
-        if idx <= table.getn(clickCasts) then
-            Clique:FillListEntry(button,idx)
-            button:Show()
-            if idx == self.listSelected then
-                button:SetBackdropBorderColor(1,1,0)
+
+    if self.spellbookButtons then
+        for idx, button in ipairs(self.spellbookButtons) do
+            if enabled and button.spellbutton:IsEnabled() == 1 then
+                button:Show()
             else
-                button:SetBackdropBorderColor(0.3, 0.3, 0.3)
+                button:Hide()
             end
-        else
-            button:Hide()
         end
     end
-    Clique:ValidateButtons()
-end
-
-local sortFunc = function(a,b)
-    local numA = tonumber(a.button) or 0
-    local numB = tonumber(b.button) or 0
-
-    if numA == numB then
-        return a.modifier < b.modifier
-    else
-        return numA < numB
-    end
 end

-function Clique:SortList()
-    self.sortList = {}
-    for k,v in pairs(self.editSet) do
-        table.insert(self.sortList, v)
-    end
-    table.sort(self.sortList, sortFunc)
+-- Spellbook button functions
+function CliqueConfig:Spellbook_EnableKeyboard(button, motion)
+    button:EnableKeyboard(true)
 end

-function Clique:ValidateButtons()
-    local entry = self.sortList[self.listSelected]
-
-    if entry then
-        CliqueButtonDelete:Enable()
-        CliqueButtonEdit:Enable()
-    else
-        CliqueButtonDelete:Disable()
-        CliqueButtonEdit:Disable()
-    end
-
-    -- This should always be enabled
-    CliqueButtonCustom:Enable()
-    CliqueButtonOptions:Enable()
-
-	-- Disable the help text
-	Clique.inuse = nil
-	for k,v in pairs(self.clicksets) do
-		if next(v) then
-			Clique.inuse = true
-		end
-	end
-	if Clique.inuse then
-		CliqueHelpText:Hide()
-	else
-		CliqueHelpText:Show()
-	end
+function CliqueConfig:Spellbook_DisableKeyboard(button, motion)
+    button:EnableKeyboard(false)
 end

-function Clique:FillListEntry(frame, idx)
-    local entry = self.sortList[idx]
-	if tonumber(entry.arg2) then
-		rank = string.format("Rank %d", entry.arg2)
-	elseif entry.arg2 then
-		rank = entry.arg2
-	end
-
-    local type = string.format("%s%s", string.upper(string.sub(entry.type, 1, 1)), string.sub(entry.type, 2))
-	local button = entry.button
-
-    frame.icon:SetTexture(entry.texture or "Interface\\Icons\\INV_Misc_QuestionMark")
-	frame.binding:SetText(entry.modifier..self:GetButtonText(button))
-
-	local arg1 = tostring(entry.arg1)
-	local arg2 = tostring(entry.arg2)
-	local arg3 = tostring(entry.arg3)
-	local arg4 = tostring(entry.arg4)
-	local arg5 = tostring(entry.arg5)
-
-	if entry.type == "action" then
-		frame.name:SetText(string.format("Action Button %d%s", arg1, entry.arg2 and (" on " .. arg2) or ""))
-	elseif entry.type == "actionbar" then
-		frame.name:SetText(string.format("Action Bar: %s", arg1))
-	elseif entry.type == "pet" then
-		local target = ""
-		if entry.arg2 then
-			target = " on " .. arg2
-		end
-		frame.name:SetText(string.format("Pet Action %d%s", arg1, target))
-	elseif entry.type == "spell" then
-		if entry.arg2 then
-			frame.name:SetText(string.format("%s (%s)%s", arg1, rank,
-				entry.arg5 and (" on " .. arg5) or ""))
-		else
-			frame.name:SetText(string.format("%s%s", arg1, entry.arg5 and " on " .. arg5 or ""))
-		end
-	elseif entry.type == "menu" then
-		frame.name:SetText("Show Menu")
-	elseif entry.type == "stop" then
-		frame.name:SetText("Cancel Pending Spell")
-	elseif entry.type == "target" then
-		frame.name:SetText(string.format("Target Unit: %s", arg1 and entry.arg1 or ""))
-	elseif entry.type == "focus" then
-		frame.name:SetText(string.format("Set Focus Unit: %s", arg1 and entry.arg1 or ""))
-	elseif entry.type == "assist" then
-		frame.name:SetText(string.format("Assist Unit: %s", arg1 and entry.arg1 or ""))
-	elseif entry.type == "item" then
-		if entry.arg1 then
-			frame.name:SetText(string.format("Item: %d,%d", arg1, arg2))
-		elseif entry.arg3 then
-			frame.name:SetText(string.format("Item: %s", arg3))
-		end
-	elseif entry.type == "macro" then
-		frame.name:SetText(string.format("Macro: %s", arg1 and entry.arg1 or string.sub(arg2, 1, 20)))
-	end
-
-    frame:Show()
-end
-
-function Clique:ButtonOnClick(button)
-    local entry = self.sortList[self.listSelected]
-
-    if button == CliqueButtonDelete then
-        if InCombatLockdown() then
-            StaticPopup_Show("CLIQUE_COMBAT_LOCKDOWN")
-            return
-        end
+invalidKeys = {
+    ["UNKNOWN"] = true,
+    ["LSHIFT"] = true,
+    ["RSHIFT"] = true,
+    ["LCTRL"] = true,
+    ["RCTRL"] = true,
+    ["LALT"] = true,
+    ["RALT"] = true,
+}

-        self.editSet[entry.modifier..entry.button] = nil
-        local len = table.getn(self.sortList) - 1
-
-        if self.listSelected > len then
-            self.listSelected = len
-        end
-
-		self:DeleteAction(entry)
-		self:UpdateClicks()
-		self:PLAYER_REGEN_ENABLED()
-		entry = nil
-
-        self:ListScrollUpdate()
-	elseif button == CliqueButtonClose then
-		self:Toggle()
-	elseif button == CliqueTextButtonClose then
-		CliqueTextListFrame:Hide()
-    elseif button == CliqueOptionsButtonClose then
-        CliqueOptionsFrame:Hide()
-    elseif button == CliqueButtonOptions then
-        if CliqueOptionsFrame:IsVisible() then
-            CliqueOptionsFrame:Hide()
-        else
-            CliqueOptionsFrame:Show()
+function CliqueConfig:Spellbook_OnBinding(button, key)
+    -- We can't bind modifiers or invalid keys
+    if invalidKeys[key] then
+        return
+    elseif key == "ESCAPE" then
+        HideUIPanel(CliqueConfig)
+        return
+    end
+
+    -- Remap any mouse buttons
+    if key == "LeftButton" then
+        key = "BUTTON1"
+    elseif key == "RightButton" then
+        key = "BUTTON2"
+    elseif key == "MiddleButton" then
+        key = "BUTTON3"
+    else
+        buttonNum = key:match("Button(%d+)")
+        if buttonNum and tonumber(buttonNum) <= 31 then
+            key = "BUTTON" .. buttonNum
         end
-    elseif button == CliqueButtonCustom then
-        if CliqueCustomFrame:IsVisible() then
-            CliqueCustomFrame:Hide()
-        else
-            CliqueCustomFrame:Show()
-		end
-	elseif button == CliqueButtonFrames then
-		if CliqueTextListFrame:IsVisible() and self.textlist == "FRAMES" then
-			CliqueTextListFrame:Hide()
-		else
-			CliqueTextListFrame:Show()
-		end
-
-		self.textlist = "FRAMES"
-		CliqueButtonDeleteProfile:Hide()
-		CliqueButtonSetProfile:Hide()
-		CliqueButtonNewProfile:Hide()
-
-		self:TextListScrollUpdate()
-		CliqueTextListFrame.title:SetText("Clique Frame Editor")
-		self.textlistSelected = nil
-	elseif button == CliqueButtonProfiles then
-		if CliqueTextListFrame:IsVisible() and self.textlist == "PROFILES" then
-			CliqueTextListFrame:Hide()
-		else
-			CliqueTextListFrame:Show()
-		end
-		self.textlist = "PROFILES"
-		self:TextListScrollUpdate()
-		CliqueButtonDeleteProfile:Show()
-		CliqueButtonSetProfile:Show()
-		CliqueButtonNewProfile:Show()
-
-		--CliqueTextListFrame.title:SetText("Profile: " .. self.db.char.profileKey)
-		self.textlistSelected = nil
-	elseif button == CliqueButtonSetProfile then
-	    local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll)
-		local selected = self.textlistSelected - offset
-		local button = getglobal("CliqueTextList"..selected)
-		self.db:SetProfile(button.name:GetText())
-	elseif button == CliqueButtonNewProfile then
-		StaticPopup_Show("CLIQUE_NEW_PROFILE")
-	elseif button == CliqueButtonDeleteProfile then
-	    local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll)
-		local selected = self.textlistSelected - offset
-		local button = getglobal("CliqueTextList"..selected)
-		self.db:DeleteProfile(button.name:GetText())
-	elseif button == CliqueButtonEdit then
-		-- Make a copy of the entry
-		self.customEntry = {}
-		for k,v in pairs(entry) do
-			self.customEntry[k] = v
-		end
-
-		CliqueCustomFrame:Show()
-
-		-- Select the right radio button
-		for k,v in pairs(self.radio) do
-			if entry.type == k.type then
-				self:CustomRadio(k)
-				k:SetChecked(true)
-			end
-		end
-
-		self.customEntry.type = entry.type
-
-		CliqueCustomArg1:SetText(entry.arg1 or "")
-		CliqueCustomArg2:SetText(entry.arg2 or "")
-		CliqueCustomArg3:SetText(entry.arg3 or "")
-		CliqueCustomArg4:SetText(entry.arg4 or "")
-		CliqueCustomArg5:SetText(entry.arg5 or "")
-
-		CliqueMultiScrollFrameEditBox:SetText(entry.arg2 or "")
-		CliqueCustomButtonIcon.icon:SetTexture(entry.texture or "Interface\\Icons\\INV_Misc_QuestionMark")
-
-		CliqueCustomButtonBinding.modifier = entry.modifier
-		CliqueCustomButtonBinding.button = self:GetButtonNumber(entry.button)
-		CliqueCustomButtonBinding:SetText(string.format("%s%s", entry.modifier, self:GetButtonText(entry.button)))
-
-		self.editEntry = entry
-
-    elseif button == CliqueCustomButtonCancel then
-		CliqueCustomFrame:Hide()
-		CliqueCustomButtonIcon.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
-		CliqueCustomButtonBinding:SetText("Set Click Binding")
-		self.customEntry = {}
-		self.editEntry = nil
-		self:CustomRadio()
-
-	elseif button == CliqueCustomButtonSave then
-		-- Add custom save logic in here
-		local entry = self.customEntry
-
-		entry.arg1 = CliqueCustomArg1:GetText()
-		entry.arg2 = CliqueCustomArg2:GetText()
-		entry.arg3 = CliqueCustomArg3:GetText()
-		entry.arg4 = CliqueCustomArg4:GetText()
-		entry.arg5 = CliqueCustomArg5:GetText()
-
-		if entry.arg1 == "" then entry.arg1 = nil end
-		if entry.arg2 == "" then entry.arg2 = nil end
-		if entry.arg3 == "" then entry.arg3 = nil end
-		if entry.arg4 == "" then entry.arg4 = nil end
-		if entry.arg5 == "" then entry.arg5 = nil end
-
-		if tonumber(entry.arg1) then entry.arg1 = tonumber(entry.arg1) end
-		if tonumber(entry.arg2) then entry.arg2 = tonumber(entry.arg2) end
-		if tonumber(entry.arg3) then entry.arg3 = tonumber(entry.arg3) end
-		if tonumber(entry.arg4) then entry.arg4 = tonumber(entry.arg4) end
-		if tonumber(entry.arg5) then entry.arg5 = tonumber(entry.arg5) end
-
-		if entry.type == "macro" then
-			local text = CliqueMultiScrollFrameEditBox:GetText()
-			if text ~= "" then
-				entry.arg2 = text
-			end
-		end
-
-		local pattern = "Hitem.+|h%[(.+)%]|h"
-		if entry.arg1 and string.find(entry.arg1, pattern) then
-			entry.arg1 = select(3, string.find(entry.arg1, pattern))
-		end
-		if entry.arg2 and string.find(entry.arg2, pattern) then
-			entry.arg2 = select(3, string.find(entry.arg2, pattern))
-		end
-		if entry.arg3 and string.find(entry.arg3, pattern) then
-			entry.arg3 = select(3, string.find(entry.arg3, pattern))
-		end
-		if entry.arg4 and string.find(entry.arg4, pattern) then
-			entry.arg4 = select(3, string.find(entry.arg4, pattern))
-		end
-		if entry.arg5 and string.find(entry.arg5, pattern) then
-			entry.arg5 = select(3, string.find(entry.arg5, pattern))
-		end
-
-		local issue
-		local arg1 = entry.arg1 and tostring(entry.arg1)
-		local arg2 = entry.arg2 and tostring(entry.arg2)
-
-		if not entry.type then
-			issue = "You must select an action type."
-		elseif not entry.button then
-			issue = "You must set a click-binding."
-		elseif entry.type == "action" and not entry.arg1 then
-			issue = "You must supply an action button number when creating a custom \"action\"."
-		elseif entry.type == "pet" and not entry.arg1 then
-			issue = "You must supply a pet action button number when creating a custom action \"pet\"."
-		elseif entry.type == "spell" and not (entry.arg1 or (entry.arg2 and entry.arg3) or entry.arg4) then
-			issue = "You must supply either a spell name and optionally an item slot/bag or name to consume when creating a \"spell\" action."
-		elseif entry.type == "item" and not ((entry.arg1 and entry.arg2) or entry.arg3) then
-			issue = "You must supply either a bag/slot, or an item name to use."
-		elseif entry.type == "macro" and arg1 and arg2 then
-			issue = "You must specify EITHER a macro index, or macro text, not both."
-		elseif entry.type == "macro" and not arg1 and not arg2 then
-			issue = "You must supply either a macro index, or macro text"
-		elseif entry.type == "actionbar" and not arg1 then
-			issue = "You must supply an action bar to change to."
-		end
-
-		if issue then
-			StaticPopupDialogs["CLIQUE_CANT_SAVE"].text = issue
-			StaticPopup_Show("CLIQUE_CANT_SAVE")
-			return
-		end
-
-		-- Delete the one we're editing, if that's the case
-		if self.editEntry then
-			local key = self.editEntry.modifier..self.editEntry.button
-			self.editSet[key] = nil
-			self:DeleteAction(self.editEntry)
-			self:UpdateClicks()
-			self.editEntry = nil
-		end
+    end

-		local key = entry.modifier..entry.button
-		self.editSet[key] = entry
-		self:UpdateClicks()
-		self:PLAYER_REGEN_ENABLED()
-		self:ButtonOnClick(CliqueCustomButtonCancel)
-	end
-
-    Clique:ValidateButtons()
-    Clique:ListScrollUpdate()
-end
+    -- TODO: Support NOT splitting the modifier keys
+    local prefix = addon:GetPrefixString(true)

-local click_func = function(self) Clique:DropDown_OnClick(self) end
+    local id = SpellBook_GetSpellID(button.spellbutton:GetID());
+    local texture = GetSpellTexture(id, SpellBookFrame.bookType)
+    local name = GetSpellName(id, SpellBookFrame.bookType)

-function Clique:DropDown_Initialize()
-    local info = {}
+    local succ, err = addon:AddBinding{
+        binding = prefix .. key,
+        type = "spell",
+        spell = name,
+        icon = texture
+    }

-    for k,v in pairs(work) do
-        info = {}
-        info.text = v
-        info.value = self.clicksets[v]
-        info.func = click_func
-        UIDropDownMenu_AddButton(info)
-	end
+    if not succ then
+        CliqueConfig:SetNotification(err)
+    else
+        CliqueConfig:UpdateList()
+    end
 end

-function Clique:DropDown_OnClick(frame)
-	UIDropDownMenu_SetSelectedValue(CliqueDropDown, frame.value)
-	Clique.editSet = frame.value
-	self.listSelected = 0
-	Clique:ListScrollUpdate()
+function CliqueConfig:SetNotification(text)
 end

-function Clique:DropDown_OnShow(frame)
-	work = {}
-	for k,v in pairs(self.clicksets) do
-		table.insert(work, k)
-	end
-	table.sort(work)
-
-	UIDropDownMenu_Initialize(frame, function() Clique:DropDown_Initialize() end);
-	UIDropDownMenu_SetSelectedValue(CliqueDropDown, self.editSet)
-	Clique:ListScrollUpdate()
+function CliqueConfig:ClearNotification()
 end

-function Clique:CustomBinding_OnClick(frame)
-	-- This handles the binding click
-	local mod = self:GetModifierText()
-	local button = arg1
-
-	if self.editSet == self.clicksets[L.CLICKSET_HARMFUL] then
-		button = string.format("%s%d", "harmbutton", self:GetButtonNumber(button))
-	elseif self.editSet == self.clicksets[L.CLICKSET_HELPFUL] then
-		button = string.format("%s%d", "helpbutton", self:GetButtonNumber(button))
-	else
-		button = self:GetButtonNumber(button)
-	end
+local memoizeBindings = setmetatable({}, {__index = function(t, k, v)
+    local binbits = addon:GetBinaryBindingKey(k)
+    rawset(t, k, binbits)
+    return binbits
+end})

-	self.customEntry.modifier = mod
-	self.customEntry.button = button
-	frame:SetText(string.format("%s%s", mod, arg1))
-end
-
-local buttonSetup = {
-	actionbar = {
-		help = L["BS_ACTIONBAR_HELP"],
-		arg1 = L["BS_ACTIONBAR_ARG1_LABEL"],
-	},
-	action = {
-		help = L["BS_ACTION_HELP"],
-		arg1 = L["BS_ACTION_ARG1_LABEL"],
-		arg2 = L["BS_ACTION_ARG2_LABEL"],
-	},
-	pet = {
-		help = L["BS_PET_HELP"],
-		arg1 = L["BS_PET_ARG1_LABEL"],
-		arg2 = L["BS_PET_ARG2_LABEL"],
-	},
-	spell = {
-		help = L["BS_SPELL_HELP"],
-		arg1 = L["BS_SPELL_ARG1_LABEL"],
-		arg2 = L["BS_SPELL_ARG2_LABEL"],
-		arg3 = L["BS_SPELL_ARG3_LABEL"],
-		arg4 = L["BS_SPELL_ARG4_LABEL"],
-		arg5 = L["BS_SPELL_ARG5_LABEL"],
-	},
-	item = {
-		help = L["BS_ITEM_HELP"],
-		arg1 = L["BS_ITEM_ARG1_LABEL"],
-		arg2 = L["BS_ITEM_ARG2_LABEL"],
-		arg3 = L["BS_ITEM_ARG3_LABEL"],
-		arg4 = L["BS_ITEM_ARG4_LABEL"],
-	},
-	macro = {
-		help = L["BS_MACRO_HELP"],
-		arg1 = L["BS_MACRO_ARG1_LABEL"],
-		arg2 = L["BS_MACRO_ARG2_LABEL"],
-	},
-	stop = {
-		help = L["BS_STOP_HELP"],
-	},
-	target = {
-		help = L["BS_TARGET_HELP"],
-		arg1 = L["BS_TARGET_ARG1_LABEL"],
-	},
-	focus = {
-		help = L["BS_FOCUS_HELP"],
-		arg1 = L["BS_FOCUS_ARG1_LABEL"],
-	},
-	assist = {
-		help = L["BS_ASSIST_HELP"],
-		arg1 = L["BS_ASSIST_ARG1_LABEL"],
-	},
-	click = {
-		help = L["BS_CLICK_HELP"],
-		arg1 = L["BS_CLICK_ARG1_LABEL"],
-	},
-	menu = {
-		help = L["BS_MENU_HELP"],
-	},
+local compareFunctions = {
+    name = function(a, b)
+        return a.binding < b.binding
+    end,
+    binding = function(a, b)
+        local mem = memoizeBindings
+        return mem[a] < mem[b]
+    end,
 }

-function Clique:CustomRadio(button)
-	local anySelected
-	for k,v in pairs(self.radio) do
-		if k ~= button then
-			k:SetChecked(nil)
-		end
-	end
-
-	if not button or not buttonSetup[button.type] then
-		CliqueCustomHelpText:SetText(L.CUSTOM_HELP)
-		CliqueCustomArg1:Hide()
-		CliqueCustomArg2:Hide()
-		CliqueCustomArg3:Hide()
-		CliqueCustomArg4:Hide()
-		CliqueCustomArg5:Hide()
-		CliqueCustomButtonBinding:SetText("Set Click Binding")
-		return
-	end
-
-	local entry = buttonSetup[button.type]
-	self.customEntry.type = button.type
-
-	if button and button.type then
-		if not button:GetChecked() then
-			self.customEntry.type = nil
-		end
-	end
-
-	-- Clear any open arguments
-	CliqueCustomArg1:SetText("")
-	CliqueCustomArg2:SetText("")
-	CliqueCustomArg3:SetText("")
-	CliqueCustomArg4:SetText("")
-	CliqueCustomArg5:SetText("")
-
-	CliqueCustomHelpText:SetText(entry.help)
-	CliqueCustomArg1.label:SetText(entry.arg1)
-	CliqueCustomArg2.label:SetText(entry.arg2)
-	CliqueCustomArg3.label:SetText(entry.arg3)
-	CliqueCustomArg4.label:SetText(entry.arg4)
-	CliqueCustomArg5.label:SetText(entry.arg5)
-
-	if entry.arg1 then CliqueCustomArg1:Show() else CliqueCustomArg1:Hide() end
-	if entry.arg2 then CliqueCustomArg2:Show() else CliqueCustomArg2:Hide() end
-	if entry.arg3 then CliqueCustomArg3:Show() else CliqueCustomArg3:Hide() end
-	if entry.arg4 then CliqueCustomArg4:Show() else CliqueCustomArg4:Hide() end
-	if entry.arg5 then CliqueCustomArg5:Show() else CliqueCustomArg5:Hide() end
-
-	-- Handle MacroText
-	if button.type == "macro" then
-		CliqueCustomArg2:Hide()
-		CliqueMulti:Show()
-		CliqueMultiScrollFrameEditBox:SetText("")
-	else
-		CliqueMulti:Hide()
-	end
-end
-
-function Clique:UpdateIconFrame()
-    local MAX_MACROS = 18;
-    local NUM_MACRO_ICONS_SHOWN = 20;
-    local NUM_ICONS_PER_ROW = 5;
-    local NUM_ICON_ROWS = 4;
-    local MACRO_ICON_ROW_HEIGHT = 36;
-    local macroPopupOffset = FauxScrollFrame_GetOffset(CliqueIconScrollFrame);
-    local numMacroIcons = GetNumMacroIcons();
-	local macroPopupIcon,macroPopupButton
+CliqueConfig.binds = {}
+function CliqueConfig:UpdateList()
+    local page = self.page1
+    local binds = Clique.profile.binds

-    -- Icon list
-    for i=1, NUM_MACRO_ICONS_SHOWN do
-        macroPopupIcon = getglobal("CliqueIcon"..i.."Icon");
-        macroPopupButton = getglobal("CliqueIcon"..i);
-
-        if not macroPopupButton.icon then
-            macroPopupButton.icon = macroPopupIcon
-        end
-
-        local index = (macroPopupOffset * NUM_ICONS_PER_ROW) + i;
-        if ( index <= numMacroIcons ) then
-            macroPopupIcon:SetTexture(GetMacroIconInfo(index));
-            macroPopupButton:Show();
-        else
-            macroPopupIcon:SetTexture("");
-            macroPopupButton:Hide();
-        end
-        macroPopupButton:SetChecked(nil);
+    -- GUI not created yet
+    if not page then
+        return
     end
-
-    FauxScrollFrame_Update(CliqueIconScrollFrame, ceil(numMacroIcons / NUM_ICONS_PER_ROW) , NUM_ICON_ROWS, MACRO_ICON_ROW_HEIGHT );
-end
-
-function Clique:SetSpellIcon(button)
-	local texture = button.icon:GetTexture()
-	self.customEntry.texture = texture
-	CliqueCustomButtonIcon.icon:SetTexture(texture)
-	CliqueIconSelectFrame:Hide()
-end
-
-StaticPopupDialogs["CLIQUE_PASSIVE_SKILL"] = {
-	text = "You can't bind a passive skill.",
-button1 = TEXT(OKAY),
-	OnAccept = function()
-	end,
-	timeout = 0,
-	hideOnEscape = 1
-}
-
-StaticPopupDialogs["CLIQUE_CANT_SAVE"] = {
-	text = "",
-	button1 = TEXT(OKAY),
-	OnAccept = function()
-	end,
-	timeout = 0,
-	hideOnEscape = 1
-}
-
-StaticPopupDialogs["CLIQUE_BINDING_PROBLEM"] = {
-	text = "That combination is already bound.  Delete the old one before trying to re-bind.",
-	button1 = TEXT(OKAY),
-	OnAccept = function()
-	end,
-	timeout = 0,
-	hideOnEscape = 1
-}
-
-StaticPopupDialogs["CLIQUE_COMBAT_LOCKDOWN"] = {
-	text = "You are currently in combat.  You cannot make changes to your click casting while in combat..",
-	button1 = TEXT(OKAY),
-	OnAccept = function()
-	end,
-	timeout = 0,
-	hideOnEscape = 1
-}
-
-StaticPopupDialogs["CLIQUE_NEW_PROFILE"] = {
-	text = TEXT("Enter the name of a new profile you'd like to create"),
-	button1 = TEXT(OKAY),
-	button2 = TEXT(CANCEL),
-	OnAccept = function(self)
-		local base = self:GetName()
-		local editbox = getglobal(base .. "EditBox")
-		Clique.db:SetProfile(editbox:GetText())
-	end,
-	timeout = 0,
-	whileDead = 1,
-	exclusive = 1,
-	showAlert = 1,
-	hideOnEscape = 1,
-	hasEditBox = 1,
-	maxLetters = 32,
-	OnShow = function(self)
-		getglobal(self:GetName().."Button1"):Disable();
-		getglobal(self:GetName().."EditBox"):SetFocus();
-	end,
-	OnHide = function(self)
-		if ( ChatFrameEditBox:IsVisible() ) then
-			ChatFrameEditBox:SetFocus();
-		end
-		getglobal(self:GetName().."EditBox"):SetText("");
-	end,
-	EditBoxOnEnterPressed = function(self)
-		if ( getglobal(self:GetParent():GetName().."Button1"):IsEnabled() == 1 ) then
-			Clique.db:SetProfile(self:GetText())
-			self:GetParent():Hide();
-		end
-	end,
-	EditBoxOnTextChanged = function (self)
-		local editBox = getglobal(self:GetParent():GetName().."EditBox");
-		local txt = editBox:GetText()
-		if #txt > 0 then
-			getglobal(self:GetParent():GetName().."Button1"):Enable();
-		else
-			getglobal(self:GetParent():GetName().."Button1"):Disable();
-		end
-	end,
-	EditBoxOnEscapePressed = function(self)
-		self:GetParent():Hide();
-		ClearCursor();
-	end
-}

-StaticPopupDialogs["CLIQUE_DELETE_PROFILE"] = {
-	text = TEXT("Enter the name of a profile you'd like to delete"),
-	button1 = TEXT(OKAY),
-	button2 = TEXT(CANCEL),
-	OnAccept = function(self)
-		Clique.db:DeleteProfile(getglobal(self:GetName().."EditBox"):GetText())
-		Clique:DropDownProfile_OnShow()
-	end,
-	timeout = 0,
-	whileDead = 1,
-	exclusive = 1,
-	showAlert = 1,
-	hideOnEscape = 1,
-	hasEditBox = 1,
-	maxLetters = 32,
-	OnShow = function(self)
-		getglobal(self:GetName().."Button1"):Disable();
-		getglobal(self:GetName().."EditBox"):SetFocus();
-	end,
-	OnHide = function(self)
-		if ( ChatFrameEditBox:IsVisible() ) then
-			ChatFrameEditBox:SetFocus();
-		end
-		getglobal(self:GetName().."EditBox"):SetText("");
-	end,
-	EditBoxOnEnterPressed = function(self)
-		if ( getglobal(self:GetParent():GetName().."Button1"):IsEnabled() == 1 ) then
-			Clique.db:DeleteProfile(self:GetText())
-			Clique:DropDownProfile_OnShow()
-			self:GetParent():Hide();
-		end
-	end,
-	EditBoxOnTextChanged = function (self)
-		local editBox = getglobal(self:GetParent():GetName().."EditBox");
-		local txt = editBox:GetText()
-		if Clique.db.profiles[txt] then
-			getglobal(self:GetParent():GetName().."Button1"):Enable();
-		else
-			getglobal(self:GetParent():GetName().."Button1"):Disable();
-		end
-	end,
-	EditBoxOnEscapePressed = function(self)
-		self:GetParent():Hide();
-		ClearCursor();
-	end
-}
-
-local work = {}
-
-function Clique:TextListScrollUpdate()
-	if not CliqueTextListScroll then return end
-
-    local idx,button
-	for k,v in pairs(work) do work[k] = nil end
-
-	if not self.textlist then self.textlist = "FRAMES" end
+    -- Sort the bindings
+    -- TODO: Use a custom sort function here

-	if self.textlist == "PROFILES" then
-		for k,v in pairs(self.db.profiles) do table.insert(work, k) end
-		table.sort(work)
-		CliqueTextListFrame.title:SetText("Profile: " .. self.db.keys.profile)
-
-	elseif self.textlist == "FRAMES" then
-		for k,v in pairs(self.ccframes) do
-			local name = k:GetName()
-			if name then
-				table.insert(work, name)
-			end
-		end
-		table.sort(work)
-	end
-
-    local offset = FauxScrollFrame_GetOffset(CliqueTextListScroll)
-    FauxScrollFrame_Update(CliqueTextListScroll, #work, 12, 22)
-
-    if not CliqueTextListScroll:IsShown() then
-        CliqueTextListFrame:SetWidth(250)
-    else
-        CliqueTextListFrame:SetWidth(275)
+    local sort = {}
+    for uid, entry in pairs(binds) do
+        sort[#sort + 1] = entry
     end
-
-    for i=1,12 do
-        idx = offset + i
-        button = getglobal("CliqueTextList"..i)
-        if idx <= #work then
-			button.name:SetText(work[idx])
-            button:Show()
-			-- Change texture
-			if self.textlist == "PROFILES" then
-				button:SetNormalTexture("Interface\\AddOns\\Clique\\images\\RadioEmpty")
-				button:SetCheckedTexture("Interface\\AddOns\\Clique\\images\\RadioChecked")
-				button:SetHighlightTexture("Interface\\AddOns\\Clique\\images\\RadioChecked")
-			else
-				button:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up")
-				button:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check")
-				button:SetHighlightTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
-			end
-
-			if self.textlistSelected == nil and self.textlist == "PROFILES" then
-				if work[idx] == self.db.keys.profile then
-					button:SetChecked(true)
-					CliqueButtonSetProfile:Disable()
-					CliqueButtonDeleteProfile:Disable()
-				else
-					button:SetChecked(nil)
-				end
-			elseif idx == self.textlistSelected and self.textlist == "PROFILES" then
-				if work[idx] == self.db.keys.profile then
-					CliqueButtonSetProfile:Disable()
-					CliqueButtonDeleteProfile:Disable()
-				else
-					CliqueButtonSetProfile:Enable()
-					CliqueButtonDeleteProfile:Enable()
-				end
-				button:SetChecked(true)
-			elseif self.textlist == "FRAMES" then
-				local name = work[idx]
-				local frame = getglobal(name)
+    table.sort(sort, compareFunctions.binding)

-				if not self.profile.blacklist then
-					self.profile.blacklist = {}
-				end
-				local bl = self.profile.blacklist
+    -- Enable or disable the scroll bar
+    if #sort > MAX_ROWS - 1 then
+        -- Set up the scrollbar for the item list
+        page.slider:SetMinMaxValues(0, #sort - MAX_ROWS)

-				if bl[name] then
-					button:SetChecked(nil)
-				else
-					button:SetChecked(true)
-				end
-            else
-                button:SetBackdropBorderColor(0.3, 0.3, 0.3)
-				button:SetChecked(nil)
+        -- Adjust and show
+        if not page.slider:IsShown() then
+            -- Adjust column positions
+            for idx, row in ipairs(self.rows) do
+                row.bind:SetWidth(100)
             end
-        else
-            button:Hide()
+            page.slider:SetValue(0)
+            page.slider:Show()
         end
-    end
-end
-
-local function makeCheckbox(parent, name, text, width)
-    local entry = CreateFrame("CheckButton", name, parent)
-    entry:SetHeight(22)
-    entry:SetWidth(width)
-    entry:SetBackdrop({insets = {left = 2, right = 2, top = 2, bottom = 2}})
-
-    entry:SetBackdropBorderColor(0.3, 0.3, 0.3)
-    entry:SetBackdropColor(0.1, 0.1, 0.1, 0.3)
-    entry:SetScript("OnEnter", function(self)
-        if self.tooltip then
-            GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
-            GameTooltip:SetText(self.tooltip)
+    elseif page.slider:IsShown() then
+        -- Move column positions back and hide the slider
+        for idx, row in ipairs(self.rows) do
+            row.bind:SetWidth(115)
         end
-    end)
-    entry:SetScript("OnLeave", function(self)
-        GameTooltip:Hide()
-    end)
-
-    local texture = entry:CreateTexture("ARTWORK")
-    texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
-    texture:SetPoint("LEFT", 0, 0)
-    texture:SetHeight(26)
-    texture:SetWidth(26)
-    entry:SetNormalTexture(texture)
-
-    local texture = entry:CreateTexture("ARTWORK")
-    texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
-    texture:SetPoint("LEFT", 0, 0)
-    texture:SetHeight(26)
-    texture:SetWidth(26)
-    texture:SetBlendMode("ADD")
-    entry:SetHighlightTexture(texture)
-
-    local texture = entry:CreateTexture("ARTWORK")
-    texture:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
-    texture:SetPoint("LEFT", 0, 0)
-    texture:SetHeight(26)
-    texture:SetWidth(26)
-    entry:SetCheckedTexture(texture)
-
-    entry.name = entry:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
-    entry.name:SetPoint("LEFT", 25, 0)
-    entry.name:SetJustifyH("LEFT")
-    entry.name:SetText(text)
-    return entry
-end
-
-function Clique:CreateOptionsWidgets(parent)
-    local button = CreateFrame("Button", "CliqueOptionsButtonClose", parent.titleBar, "UIPanelCloseButton")
-    button:SetHeight(25)
-    button:SetWidth(25)
-    button:SetPoint("TOPRIGHT", -5, 3)
-    button:SetScript("OnClick", function(self) Clique:ButtonOnClick(self) end)
-
-    local downClick = makeCheckbox(parent, "CliqueOptionsAnyDown", L.DOWNCLICK_LABEL, 300)
-    downClick:SetPoint("TOPLEFT", 5, -25)
-
-    local switchSpec = makeCheckbox(parent, "CliqueOptionsSpecSwitch", L.SPECSWITCH_LABEL, 300)
-    switchSpec:SetPoint("TOPLEFT", 5, -45)
-
-    local priDropdown = CreateFrame("Frame", "CliquePriSpecDropDown", parent, "UIDropDownMenuTemplate")
-    priDropdown:ClearAllPoints()
-    priDropdown:SetPoint("TOPLEFT", switchSpec, "BOTTOMLEFT", 65, 0)
-    priDropdown:Show()
-    priDropdown.label = priDropdown:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-    priDropdown.label:SetText(L["Primary:"])
-    priDropdown.label:SetPoint("RIGHT", priDropdown, "LEFT", 0, 0)
-    priDropdown.label:SetHeight(16)
-
-    local function initialize(self, level)
-        local function OnClick(self)
-            UIDropDownMenu_SetSelectedID(priDropdown, self:GetID())
-            Clique.db.char.primaryProfile = self.value
-            Clique:UpdateClicks()
-        end
-
-        local work = {}
-        for k,v in pairs(Clique.db.profiles) do
-            table.insert(work, k)
-        end
-		table.sort(work)
-
-        for idx,profile in ipairs(work) do
-            local info = UIDropDownMenu_CreateInfo()
-            info.text = profile
-            info.func = OnClick
-            info.value = profile
-            UIDropDownMenu_AddButton(info, level)
-        end
-    end
-
-    UIDropDownMenu_Initialize(priDropdown, initialize)
-    UIDropDownMenu_SetWidth(priDropdown, 175);
-    UIDropDownMenu_SetButtonWidth(priDropdown, 199)
-    UIDropDownMenu_JustifyText(priDropdown, "LEFT")
-    if Clique.db.char.primaryProfile then
-        UIDropDownMenu_SetSelectedValue(priDropdown, Clique.db.char.primaryProfile)
-    else
-        UIDropDownMenu_SetSelectedValue(priDropdown, Clique.db.keys.profile)
+        page.slider:Hide()
     end

-    local secDropdown = CreateFrame("Frame", "CliqueSecSpecDropDown", parent, "UIDropDownMenuTemplate")
-    secDropdown:ClearAllPoints()
-    secDropdown:SetPoint("TOPLEFT", priDropdown, "BOTTOMLEFT", 0, 0)
-    secDropdown:Show()
-    secDropdown.label = secDropdown:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
-    secDropdown.label:SetText(L["Secondary:"])
-    secDropdown.label:SetPoint("RIGHT", secDropdown, "LEFT", 0, 0)
-    secDropdown.label:SetHeight(16)
-
-    local function initialize(self, level)
-        local function OnClick(self)
-            UIDropDownMenu_SetSelectedID(secDropdown, self:GetID())
-            Clique.db.char.secondaryProfile = self.value
-            Clique:UpdateClicks()
-        end
-
-        local work = {}
-        for k,v in pairs(Clique.db.profiles) do
-            table.insert(work, k)
-        end
-		table.sort(work)
-
-        for idx,profile in ipairs(work) do
-            local info = UIDropDownMenu_CreateInfo()
-            info.text = profile
-            info.func = OnClick
-            info.value = profile
-            UIDropDownMenu_AddButton(info, level)
+    -- Update the rows in the list
+    local offset = page.slider:GetValue() or 0
+    for idx, row in ipairs(self.rows) do
+        local offsetIndex = offset + idx
+        if sort[offsetIndex] then
+            local bind = sort[offsetIndex]
+            row.icon:SetTexture(addon:GetBindingIcon(bind))
+            row.name:SetText(addon:GetBindingActionText(bind))
+            --row.type:SetText(bind.type)
+            row.bind:SetText(addon:GetBindingKeyComboText(bind))
+            row:Show()
+        else
+            row:Hide()
         end
     end
+end

-    UIDropDownMenu_Initialize(secDropdown, initialize)
-    UIDropDownMenu_SetWidth(secDropdown, 175);
-    UIDropDownMenu_SetButtonWidth(secDropdown, 199)
-    UIDropDownMenu_JustifyText(secDropdown, "LEFT")
-    if Clique.db.char.secondaryProfile then
-        UIDropDownMenu_SetSelectedValue(secDropdown, Clique.db.char.secondaryProfile)
-    else
-        UIDropDownMenu_SetSelectedValue(secDropdown, Clique.db.keys.profile)
-    end
+function CliqueConfig:ClearEditPage()
+end

-    local function refreshOptions(self)
-        -- Hide the dropdowns if the spec switch option isn't selected
-        local switchSpec = Clique.db.char.switchSpec
-        local downClick = Clique.db.char.downClick
-        CliqueOptionsSpecSwitch:SetChecked(switchSpec)
-        CliqueOptionsAnyDown:SetChecked(downClick)
+function CliqueConfig:ShowEditPage()
+    self:ClearEditPage()
+    self.page1:Hide()
+    self.page2:Show()
+end

-        if switchSpec then
-            CliquePriSpecDropDown:Show()
-            CliqueSecSpecDropDown:Show()
-            if not Clique.db.char.primaryProfile then
-                Clique.db.char.primaryProfile = Clique.db.keys.profile
-            end
-            if not Clique.db.char.secondaryProfile then
-                Clique.db.char.secondaryProfile = Clique.db.keys.profile
-            end
-        else
-            CliquePriSpecDropDown:Hide()
-            CliqueSecSpecDropDown:Hide()
-        end
-    end
+function CliqueConfig:Save_OnClick(button, button, down)
+end

-    parent:SetScript("OnShow", refreshOptions)
-    switchSpec:SetScript("OnClick", function(self)
-        if Clique.db.char.switchSpec then
-            Clique.db.char.switchSpec = false
-        else
-            Clique.db.char.switchSpec = true
-        end
-        refreshOptions(parent)
-        Clique:UpdateClicks()
-    end)
-    downClick:SetScript("OnClick", function(self)
-        if Clique.db.char.downClick then
-            Clique.db.char.downClick = false
-        else
-            Clique.db.char.downClick = true
-        end
-        refreshOptions(parent)
-        Clique:SetClickType()
-    end)
+function CliqueConfig:Cancel_OnClick(button, button, down)
+    self:ClearEditPage()
+    self.page2:Hide()
+    self.page1:Show()
 end
+
diff --git a/CliqueUtils.lua b/CliqueUtils.lua
index a2f49a7..58dc082 100644
--- a/CliqueUtils.lua
+++ b/CliqueUtils.lua
@@ -1,47 +1,129 @@
-local buttonMap = setmetatable({
-    [1] = "LeftButton",
-    [2] = "RightButton",
-    [3] = "MiddleButton",
-}, {
-    __index = function(t, k)
-        return "Button" .. k
-    end
-})
-
-function Clique:GetModifierText()
-    local modifier = ""
-
-	if IsShiftKeyDown() then
-		modifier = "Shift-"..modifier
-	end
-	if IsControlKeyDown() then
-		modifier = "Ctrl-"..modifier
-	end
-	if IsAltKeyDown() then
-		modifier = "Alt-"..modifier
-	end
-
-    return modifier
+local addonName, addon = ...
+local L = addon.L
+
+-- Returns the prefix string for the current keyboard state.
+--
+-- Arguments:
+--   split - Whether or not to split the modifier keys into left and right components
+
+function addon:GetPrefixString(split)
+    shift, lshift, rshift = IsShiftKeyDown(), IsLeftShiftKeyDown(), IsRightShiftKeyDown()
+    ctrl, lctrl, rctrl = IsControlKeyDown(), IsLeftControlKeyDown(), IsRightControlKeyDown()
+    alt, lalt, ralt = IsAltKeyDown(), IsLeftAltKeyDown() IsRightAltKeyDown()
+
+    if not extended then
+        shift = shift or lshift or rshift
+        ctrl = ctrl or lctrl or rctrl
+        alt = alt or lalt or ralt
+
+        lshift, rshift = false, false
+        lctrl, rctrl = false, false
+        lalt, ralt = false, false
+    end
+
+    local prefix = ""
+    if shift then
+        prefix = ((lshift and "LSHIFT-") or (rshift and "RSHIFT-") or "SHIFT-") .. prefix
+    end
+    if ctrl then
+        prefix = ((lctrl and "LCTRL-") or (rctrl and "RCTRL-") or "CTRL-") .. prefix
+    end
+    if alt then
+        prefix = ((lalt and "LALT-") or (ralt and "RALT-") or "ALT-") .. prefix
+    end
+
+    return prefix
 end

-function Clique:GetButtonNumber(button)
-    return SecureButton_GetButtonSuffix(button)
+local convertMap = {
+    LSHIFT = L["LShift"],
+    RSHIFT = L["RShift"],
+    SHIFT = L["Shift"],
+    LCTRL = L["LCtrl"],
+    RCTRL = L["RCtrl"],
+    CTRL = L["Ctrl"],
+    LALT = L["LAlt"],
+    RALT = L["RAlt"],
+    ALT = L["Alt"],
+    BUTTON1 = L["LeftButton"],
+    BUTTON2 = L["RightButton"],
+    BUTTON3 = L["MiddleButton"],
+}
+
+local function convert(item, ...)
+    if not item then
+        return ""
+    else
+        local mapItem = convertMap[item]
+        item = mapItem and mapItem or item
+
+        if select("#", ...) > 0 then
+            return item, "-", convert(...)
+        else
+            return item, convert(...)
+        end
+    end
+end
+
+function addon:GetBindingIcon(binding)
+    local btype = binding.type
+    if btype == "menu" then
+        --return "Interface\\Icons\\Trade_Engineering"
+        return nil
+    elseif btype == "target" then
+        --return "Interface\\Icons\\Ability_Mage_IncantersAbsorbtion"
+        return nil
+    else
+        return binding.icon or "Interface\\Icons\\INV_Misc_QuestionMark"
+    end
 end

-function Clique:GetButtonText(num)
-    if not num then return "" end
-	if type(num) == "string" then
-		num = num:gsub("helpbutton", "")
-		num = num:gsub("harmbutton", "")
-	end
-	num = tonumber(num) and tonumber(num) or num
-    return buttonMap[num] or ""
+function addon:GetBindingKeyComboText(binding)
+    return strconcat(convert(strsplit("-", binding.binding)))
 end

-function Clique:CheckBinding(key)
-    for k,v in pairs(self.editSet) do
-        if k == key then
-            return v
+function addon:GetBindingActionText(binding)
+    local btype = binding.type
+    if btype == "menu" then
+        return L["Show unit menu"]
+    elseif btype == "target" then
+        return L["Target '%s' unit"]:format(binding.unit)
+    elseif btype == "spell" then
+        if binding.rank then
+            return L["Cast %s (Rank %d)"]:format(binding.spell, binding.rank)
+        end
+        return L["Cast %s"]:format(binding.spell)
+    else
+        for k,v in pairs(binding) do
+            print("binding", k, v)
+        end
+
+        return L["Unknown binding type '%s'"]:format(tostring(btype))
+    end
+end
+
+local binMap = {
+    ALT = 1,
+    LALT = 2,
+    RALT = 3,
+    CTRL = 4,
+    LCTRL = 5,
+    LCTRL = 6,
+    SHIFT = 7,
+    LSHIFT = 8,
+    RSHIFT = 9,
+}
+
+function addon:GetBinaryBindingKey(binding)
+    ret = {"0", "0", "0", "0", "0", "0", "0", "0", "0"}
+    splits = {strsplit("-", binding.binding)}
+    for idx, modifier in ipairs(splits) do
+        local bit = binMap[modifier]
+        if bit then
+            ret[bit] = "1"
+        else
+            ret[10] = modifier
         end
     end
+    return table.concat(ret)
 end
diff --git a/Dongle.lua b/Dongle.lua
deleted file mode 100644
index c37758b..0000000
--- a/Dongle.lua
+++ /dev/null
@@ -1,1434 +0,0 @@
---[[-------------------------------------------------------------------------
-  Copyright (c) 2006-2007, Dongle Development Team
-  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions are
-  met:
-
-      * Redistributions of source code must retain the above copyright
-        notice, this list of conditions and the following disclaimer.
-      * Redistributions in binary form must reproduce the above
-        copyright notice, this list of conditions and the following
-        disclaimer in the documentation and/or other materials provided
-        with the distribution.
-      * Neither the name of the Dongle Development Team nor the names of
-        its contributors may be used to endorse or promote products derived
-        from this software without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------------]]
-local major = "DongleStub"
-local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1)
-
-local g = getfenv(0)
-
-if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then
-	local lib = setmetatable({}, {
-		__call = function(t,k)
-			if type(t.versions) == "table" and t.versions[k] then
-				return t.versions[k].instance
-			else
-				error("Cannot find a library with name '"..tostring(k).."'", 2)
-			end
-		end
-	})
-
-	function lib:IsNewerVersion(major, minor)
-		local versionData = self.versions and self.versions[major]
-
-		-- If DongleStub versions have differing major version names
-		-- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second
-		-- instance will be loaded, with older logic.  This code attempts
-		-- to compensate for that by matching the major version against
-		-- "^DongleStub", and handling the version check correctly.
-
-		if major:match("^DongleStub") then
-			local oldmajor,oldminor = self:GetVersion()
-			if self.versions and self.versions[oldmajor] then
-				return minor > oldminor
-			else
-				return true
-			end
-		end
-
-		if not versionData then return true end
-		local oldmajor,oldminor = versionData.instance:GetVersion()
-		return minor > oldminor
-	end
-
-	local function NilCopyTable(src, dest)
-		for k,v in pairs(dest) do dest[k] = nil end
-		for k,v in pairs(src) do dest[k] = v end
-	end
-
-	function lib:Register(newInstance, activate, deactivate)
-		assert(type(newInstance.GetVersion) == "function",
-			"Attempt to register a library with DongleStub that does not have a 'GetVersion' method.")
-
-		local major,minor = newInstance:GetVersion()
-		assert(type(major) == "string",
-			"Attempt to register a library with DongleStub that does not have a proper major version.")
-		assert(type(minor) == "number",
-			"Attempt to register a library with DongleStub that does not have a proper minor version.")
-
-		-- Generate a log of all library registrations
-		if not self.log then self.log = {} end
-		table.insert(self.log, string.format("Register: %s, %s", major, minor))
-
-		if not self:IsNewerVersion(major, minor) then return false end
-		if not self.versions then self.versions = {} end
-
-		local versionData = self.versions[major]
-		if not versionData then
-			-- New major version
-			versionData = {
-				["instance"] = newInstance,
-				["deactivate"] = deactivate,
-			}
-
-			self.versions[major] = versionData
-			if type(activate) == "function" then
-				table.insert(self.log, string.format("Activate: %s, %s", major, minor))
-				activate(newInstance)
-			end
-			return newInstance
-		end
-
-		local oldDeactivate = versionData.deactivate
-		local oldInstance = versionData.instance
-
-		versionData.deactivate = deactivate
-
-		local skipCopy
-		if type(activate) == "function" then
-			table.insert(self.log, string.format("Activate: %s, %s", major, minor))
-			skipCopy = activate(newInstance, oldInstance)
-		end
-
-		-- Deactivate the old libary if necessary
-		if type(oldDeactivate) == "function" then
-			local major, minor = oldInstance:GetVersion()
-			table.insert(self.log, string.format("Deactivate: %s, %s", major, minor))
-			oldDeactivate(oldInstance, newInstance)
-		end
-
-		-- Re-use the old table, and discard the new one
-		if not skipCopy then
-			NilCopyTable(newInstance, oldInstance)
-		end
-		return oldInstance
-	end
-
-	function lib:GetVersion() return major,minor end
-
-	local function Activate(new, old)
-		-- This code ensures that we'll move the versions table even
-		-- if the major version names are different, in the case of
-		-- DongleStub
-		if not old then old = g.DongleStub end
-
-		if old then
-			new.versions = old.versions
-			new.log = old.log
-		end
-		g.DongleStub = new
-	end
-
-	-- Actually trigger libary activation here
-	local stub = g.DongleStub or lib
-	lib = stub:Register(lib, Activate)
-end
-
---[[-------------------------------------------------------------------------
-  Begin Library Implementation
----------------------------------------------------------------------------]]
-
-local major = "Dongle-1.2"
-local minor = tonumber(string.match("$Revision: 674 $", "(%d+)") or 1)
-
-assert(DongleStub, string.format("%s requires DongleStub.", major))
-
-if not DongleStub:IsNewerVersion(major, minor) then return end
-
-local Dongle = {}
-local methods = {
-	"RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsEventRegistered",
-	"RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "TriggerMessage", "IsMessageRegistered",
-	"ScheduleTimer", "ScheduleRepeatingTimer", "CancelTimer", "IsTimerScheduled",
-	"EnableDebug", "IsDebugEnabled", "Print", "PrintF", "Debug", "DebugF", "Echo", "EchoF",
-	"InitializeDB",
-	"InitializeSlashCommand",
-	"NewModule", "HasModule", "IterateModules",
-}
-
-local registry = {}
-local lookup = {}
-local loadqueue = {}
-local loadorder = {}
-local events = {}
-local databases = {}
-local commands = {}
-local messages = {}
-local timers = {}
-local heap = {}
-
-local frame
-
---[[-------------------------------------------------------------------------
-	Message Localization
----------------------------------------------------------------------------]]
-
-local L = {
-	["ADDMESSAGE_REQUIRED"] = "The frame you specify must have an 'AddMessage' method.",
-	["ALREADY_REGISTERED"] = "A Dongle with the name '%s' is already registered.",
-	["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)",
-	["BAD_ARGUMENT_DB"] = "bad argument #%d to '%s' (DongleDB expected)",
-	["CANNOT_DELETE_ACTIVE_PROFILE"] = "You cannot delete your active profile. Change profiles, then attempt to delete.",
-	["DELETE_NONEXISTANT_PROFILE"] = "You cannot delete a non-existant profile.",
-	["MUST_CALLFROM_DBOBJECT"] = "You must call '%s' from a Dongle database object.",
-	["MUST_CALLFROM_REGISTERED"] = "You must call '%s' from a registered Dongle.",
-	["MUST_CALLFROM_SLASH"] = "You must call '%s' from a Dongle slash command object.",
-	["PROFILE_DOES_NOT_EXIST"] = "Profile '%s' doesn't exist.",
-	["REPLACE_DEFAULTS"] = "You are attempting to register defaults with a database that already contains defaults.",
-	["SAME_SOURCE_DEST"] = "Source/Destination profile cannot be the same profile.",
-	["EVENT_REGISTER_SPECIAL"] = "You cannot register for the '%s' event. Use the '%s' method instead.",
-	["Unknown"] = "Unknown",
-	["INJECTDB_USAGE"] = "Usage: DongleCmd:InjectDBCommands(db, ['copy', 'delete', 'list', 'reset', 'set'])",
-	["DBSLASH_PROFILE_COPY_DESC"] = "profile copy <name> - Copies profile <name> into your current profile.",
-	["DBSLASH_PROFILE_COPY_PATTERN"] = "^profile copy (.+)$",
-	["DBSLASH_PROFILE_DELETE_DESC"] = "profile delete <name> - Deletes the profile <name>.",
-	["DBSLASH_PROFILE_DELETE_PATTERN"] = "^profile delete (.+)$",
-	["DBSLASH_PROFILE_LIST_DESC"] = "profile list - Lists all valid profiles.",
-	["DBSLASH_PROFILE_LIST_PATTERN"] = "^profile list$",
-	["DBSLASH_PROFILE_RESET_DESC"] = "profile reset - Resets the current profile.",
-	["DBSLASH_PROFILE_RESET_PATTERN"] = "^profile reset$",
-	["DBSLASH_PROFILE_SET_DESC"] = "profile set <name> - Sets the current profile to <name>.",
-	["DBSLASH_PROFILE_SET_PATTERN"] = "^profile set (.+)$",
-	["DBSLASH_PROFILE_LIST_OUT"] = "Profile List:",
-}
-
---[[-------------------------------------------------------------------------
-	Utility functions for Dongle use
----------------------------------------------------------------------------]]
-
-local function assert(level,condition,message)
-	if not condition then
-		error(message,level)
-	end
-end
-
-local function argcheck(value, num, ...)
-	if type(num) ~= "number" then
-		error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1)
-	end
-
-	for i=1,select("#", ...) do
-		if type(value) == select(i, ...) then return end
-	end
-
-	local types = strjoin(", ", ...)
-	local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]")
-	error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3)
-end
-
-local function safecall(func,...)
-	local success,err = pcall(func,...)
-	if not success then
-		geterrorhandler()(err)
-	end
-end
-
---[[-------------------------------------------------------------------------
-	Dongle constructor, and DongleModule system
----------------------------------------------------------------------------]]
-
-function Dongle:New(name, obj)
-	argcheck(name, 2, "string")
-	argcheck(obj, 3, "table", "nil")
-
-	if not obj then
-		obj = {}
-	end
-
-	if registry[name] then
-		error(string.format(L["ALREADY_REGISTERED"], name))
-	end
-
-	local reg = {["obj"] = obj, ["name"] = name}
-
-	registry[name] = reg
-	lookup[obj] = reg
-	lookup[name] = reg
-
-	for k,v in pairs(methods) do
-		obj[v] = self[v]
-	end
-
-	-- Add this Dongle to the end of the queue
-	table.insert(loadqueue, obj)
-	return obj,name
-end
-
-function Dongle:NewModule(name, obj)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "NewModule"))
-	argcheck(name, 2, "string")
-	argcheck(obj, 3, "table", "nil")
-
-	obj,name = Dongle:New(name, obj)
-
-	if not reg.modules then reg.modules = {} end
-	reg.modules[obj] = obj
-	reg.modules[name] = obj
-
-	return obj,name
-end
-
-function Dongle:HasModule(module)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "HasModule"))
-	argcheck(module, 2, "string", "table")
-
-	return reg.modules and reg.modules[module]
-end
-
-local function ModuleIterator(t, name)
-	if not t then return end
-	local obj
-	repeat
-		name,obj = next(t, name)
-	until type(name) == "string" or not name
-
-	return name,obj
-end
-
-function Dongle:IterateModules()
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IterateModules"))
-
-	return ModuleIterator, reg.modules
-end
-
---[[-------------------------------------------------------------------------
-	Event registration system
----------------------------------------------------------------------------]]
-
-local function OnEvent(frame, event, ...)
-	local eventTbl = events[event]
-	if eventTbl then
-		for obj,func in pairs(eventTbl) do
-			if type(func) == "string" then
-				if type(obj[func]) == "function" then
-					safecall(obj[func], obj, event, ...)
-				end
-			else
-				safecall(func, event, ...)
-			end
-		end
-	end
-end
-
-local specialEvents = {
-	["PLAYER_LOGIN"] = "Enable",
-	["PLAYER_LOGOUT"] = "Disable",
-}
-
-function Dongle:RegisterEvent(event, func)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "RegisterEvent"))
-	argcheck(event, 2, "string")
-	argcheck(func, 3, "string", "function", "nil")
-
-	local special = (self ~= Dongle) and specialEvents[event]
-	if special then
-		error(string.format(L["EVENT_REGISTER_SPECIAL"], event, special), 3)
-	end
-
-	-- Name the method the same as the event if necessary
-	if not func then func = event end
-
-	if not events[event] then
-		events[event] = {}
-		frame:RegisterEvent(event)
-	end
-	events[event][self] = func
-end
-
-function Dongle:UnregisterEvent(event)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterEvent"))
-	argcheck(event, 2, "string")
-
-	local tbl = events[event]
-	if tbl then
-		tbl[self] = nil
-		if not next(tbl) then
-			events[event] = nil
-			frame:UnregisterEvent(event)
-		end
-	end
-end
-
-function Dongle:UnregisterAllEvents()
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "UnregisterAllEvents"))
-
-	for event,tbl in pairs(events) do
-		tbl[self] = nil
-		if not next(tbl) then
-			events[event] = nil
-			frame:UnregisterEvent(event)
-		end
-	end
-end
-
-function Dongle:IsEventRegistered(event)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "IsEventRegistered"))
-	argcheck(event, 2, "string")
-
-	if events[event] and events[event][self] then
-		return true
-	else
-		return false
-	end
-end
-
---[[-------------------------------------------------------------------------
-	Inter-Addon Messaging System
----------------------------------------------------------------------------]]
-
-function Dongle:RegisterMessage(msg, func)
-	argcheck(self, 1, "table")
-	argcheck(msg, 2, "string")
-	argcheck(func, 3, "string", "function", "nil")
-
-	-- Name the method the same as the message if necessary
-	if not func then func = msg end
-
-	if not messages[msg] then
-		messages[msg] = {}
-	end
-	messages[msg][self] = func
-end
-
-function Dongle:UnregisterMessage(msg)
-	argcheck(self, 1, "table")
-	argcheck(msg, 2, "string")
-
-	local tbl = messages[msg]
-	if tbl then
-		tbl[self] = nil
-		if not next(tbl) then
-			messages[msg] = nil
-		end
-	end
-end
-
-function Dongle:UnregisterAllMessages()
-	argcheck(self, 1, "table")
-
-	for msg,tbl in pairs(messages) do
-		tbl[self] = nil
-		if not next(tbl) then
-			messages[msg] = nil
-		end
-	end
-end
-
-function Dongle:TriggerMessage(msg, ...)
-	argcheck(self, 1, "table")
-	argcheck(msg, 2, "string")
-	local msgTbl = messages[msg]
-	if not msgTbl then return end
-
-	for obj,func in pairs(msgTbl) do
-		if type(func) == "string" then
-			if type(obj[func]) == "function" then
-				safecall(obj[func], obj, msg, ...)
-			end
-		else
-			safecall(func, msg, ...)
-		end
-	end
-end
-
-function Dongle:IsMessageRegistered(msg)
-	argcheck(self, 1, "table")
-	argcheck(msg, 2, "string")
-
-	local tbl = messages[msg]
-	return tbl[self]
-end
-
---[[-------------------------------------------------------------------------
-	Timer System
----------------------------------------------------------------------------]]
-
-local function HeapSwap(i1, i2)
-	heap[i1], heap[i2] = heap[i2], heap[i1]
-end
-
-local function HeapBubbleUp(index)
-	while index > 1 do
-		local parentIndex = math.floor(index / 2)
-		if heap[index].timeToFire < heap[parentIndex].timeToFire then
-			HeapSwap(index, parentIndex)
-			index = parentIndex
-		else
-			break
-		end
-	end
-end
-
-local function HeapBubbleDown(index)
-	while 2 * index <= heap.lastIndex do
-		local leftIndex = 2 * index
-		local rightIndex = leftIndex + 1
-		local current = heap[index]
-		local leftChild = heap[leftIndex]
-		local rightChild = heap[rightIndex]
-
-		if not rightChild then
-			if leftChild.timeToFire < current.timeToFire then
-				HeapSwap(index, leftIndex)
-				index = leftIndex
-			else
-				break
-			end
-		else
-			if leftChild.timeToFire < current.timeToFire or
-			   rightChild.timeToFire < current.timeToFire then
-				if leftChild.timeToFire < rightChild.timeToFire then
-					HeapSwap(index, leftIndex)
-					index = leftIndex
-				else
-					HeapSwap(index, rightIndex)
-					index = rightIndex
-				end
-			else
-				break
-			end
-		end
-	end
-end
-
-local function OnUpdate(frame, elapsed)
-	local schedule = heap[1]
-	while schedule and schedule.timeToFire < GetTime() do
-		if schedule.cancelled then
-			HeapSwap(1, heap.lastIndex)
-			heap[heap.lastIndex] = nil
-			heap.lastIndex = heap.lastIndex - 1
-			HeapBubbleDown(1)
-		else
-			if schedule.args then
-				safecall(schedule.func, schedule.name, unpack(schedule.args))
-			else
-				safecall(schedule.func, schedule.name)
-			end
-
-			if schedule.repeating then
-				schedule.timeToFire = schedule.timeToFire + schedule.repeating
-				HeapBubbleDown(1)
-			else
-				HeapSwap(1, heap.lastIndex)
-				heap[heap.lastIndex] = nil
-				heap.lastIndex = heap.lastIndex - 1
-				HeapBubbleDown(1)
-				timers[schedule.name] = nil
-			end
-		end
-		schedule = heap[1]
-	end
-	if not schedule then frame:Hide() end
-end
-
-function Dongle:ScheduleTimer(name, func, delay, ...)
-	argcheck(self, 1, "table")
-	argcheck(name, 2, "string")
-	argcheck(func, 3, "function")
-	argcheck(delay, 4, "number")
-
-	if Dongle:IsTimerScheduled(name) then
-		Dongle:CancelTimer(name)
-	end
-
-	local schedule = {}
-	timers[name] = schedule
-	schedule.timeToFire = GetTime() + delay
-	schedule.func = func
-	schedule.name = name
-	if select('#', ...) ~= 0 then
-		schedule.args = { ... }
-	end
-
-	if heap.lastIndex then
-		heap.lastIndex = heap.lastIndex + 1
-	else
-		heap.lastIndex = 1
-	end
-	heap[heap.lastIndex] = schedule
-	HeapBubbleUp(heap.lastIndex)
-	if not frame:IsShown() then
-		frame:Show()
-	end
-end
-
-function Dongle:ScheduleRepeatingTimer(name, func, delay, ...)
-	Dongle:ScheduleTimer(name, func, delay, ...)
-	timers[name].repeating = delay
-end
-
-function Dongle:IsTimerScheduled(name)
-	argcheck(self, 1, "table")
-	argcheck(name, 2, "string")
-	local schedule = timers[name]
-	if schedule then
-		return true, schedule.timeToFire - GetTime()
-	else
-		return false
-	end
-end
-
-function Dongle:CancelTimer(name)
-	argcheck(self, 1, "table")
-	argcheck(name, 2, "string")
-	local schedule = timers[name]
-	if not schedule then return end
-	schedule.cancelled = true
-	timers[name] = nil
-end
-
---[[-------------------------------------------------------------------------
-	Debug and Print utility functions
----------------------------------------------------------------------------]]
-
-function Dongle:EnableDebug(level, frame)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug"))
-	argcheck(level, 2, "number", "nil")
-	argcheck(frame, 3, "table", "nil")
-
-	assert(3, type(frame) == "nil" or type(frame.AddMessage) == "function", L["ADDMESSAGE_REQUIRED"])
-	reg.debugFrame = frame or ChatFrame1
-	reg.debugLevel = level
-end
-
-function Dongle:IsDebugEnabled()
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EnableDebug"))
-
-	return reg.debugLevel, reg.debugFrame
-end
-
-local function argsToStrings(a1, ...)
-	if select("#", ...) > 0 then
-		return tostring(a1), argsToStrings(...)
-	else
-		return tostring(a1)
-	end
-end
-
-local function printHelp(obj, method, header, frame, msg, ...)
-	local reg = lookup[obj]
-	assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method))
-
-	local name = reg.name
-
-	if header then
-		msg = "|cFF33FF99"..name.."|r: "..tostring(msg)
-	end
-
-	if select("#", ...) > 0 then
-		msg = string.join(", ", msg, argsToStrings(...))
-	end
-
-	frame:AddMessage(msg)
-end
-
-local function printFHelp(obj, method, header, frame, msg, ...)
-	local reg = lookup[obj]
-	assert(4, reg, string.format(L["MUST_CALLFROM_REGISTERED"], method))
-
-	local name = reg.name
-	local success,txt
-
-	if header then
-		msg = "|cFF33FF99%s|r: " .. msg
-		success,txt = pcall(string.format, msg, name, ...)
-	else
-		success,txt = pcall(string.format, msg, ...)
-	end
-
-	if success then
-		frame:AddMessage(txt)
-	else
-		error(string.gsub(txt, "'%?'", string.format("'%s'", method)), 3)
-	end
-end
-
-function Dongle:Print(msg, ...)
-	local reg = lookup[self]
-	assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Print"))
-	argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata")
-	return printHelp(self, "Print", true, DEFAULT_CHAT_FRAME, msg, ...)
-end
-
-function Dongle:PrintF(msg, ...)
-	local reg = lookup[self]
-	assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "PrintF"))
-	argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata")
-	return printFHelp(self, "PrintF", true, DEFAULT_CHAT_FRAME, msg, ...)
-end
-
-function Dongle:Echo(msg, ...)
-	local reg = lookup[self]
-	assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Echo"))
-	argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata")
-	return printHelp(self, "Echo", false, DEFAULT_CHAT_FRAME, msg, ...)
-end
-
-function Dongle:EchoF(msg, ...)
-	local reg = lookup[self]
-	assert(1, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "EchoF"))
-	argcheck(msg, 2, "number", "string", "boolean", "table", "function", "thread", "userdata")
-	return printFHelp(self, "EchoF", false, DEFAULT_CHAT_FRAME, msg, ...)
-end
-
-function Dongle:Debug(level, ...)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "Debug"))
-	argcheck(level, 2, "number")
-
-	if reg.debugLevel and level <= reg.debugLevel then
-		printHelp(self, "Debug", true, reg.debugFrame, ...)
-	end
-end
-
-function Dongle:DebugF(level, ...)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "DebugF"))
-	argcheck(level, 2, "number")
-
-	if reg.debugLevel and level <= reg.debugLevel then
-		printFHelp(self, "DebugF", true, reg.debugFrame, ...)
-	end
-end
-
---[[-------------------------------------------------------------------------
-	Database System
----------------------------------------------------------------------------]]
-
-local dbMethods = {
-	"RegisterDefaults", "SetProfile", "GetProfiles", "DeleteProfile", "CopyProfile",
-	"GetCurrentProfile", "ResetProfile", "ResetDB",
-	"RegisterNamespace",
-}
-
-local function copyTable(src)
-	local dest = {}
-	for k,v in pairs(src) do
-		if type(k) == "table" then
-			k = copyTable(k)
-		end
-		if type(v) == "table" then
-			v = copyTable(v)
-		end
-		dest[k] = v
-	end
-	return dest
-end
-
-local function copyDefaults(dest, src, force)
-	for k,v in pairs(src) do
-		if k == "*" then
-			if type(v) == "table" then
-				-- Values are tables, need some magic here
-				local mt = {
-					__cache = {},
-					__index = function(t,k)
-						local mt = getmetatable(dest)
-						local cache = rawget(mt, "__cache")
-						local tbl = rawget(cache, k)
-						if not tbl then
-							local parent = t
-							local parentkey = k
-							tbl = copyTable(v)
-							rawset(cache, k, tbl)
-							local mt = getmetatable(tbl)
-							if not mt then
-								mt = {}
-								setmetatable(tbl, mt)
-							end
-							local newindex = function(t,k,v)
-								rawset(parent, parentkey, t)
-								rawset(t, k, v)
-							end
-							rawset(mt, "__newindex", newindex)
-						end
-						return tbl
-					end,
-				}
-				setmetatable(dest, mt)
-				-- Now need to set the metatable on any child tables
-				for dkey,dval in pairs(dest) do
-					copyDefaults(dval, v)
-				end
-			else
-				-- Values are not tables, so this is just a simple return
-				local mt = {__index = function() return v end}
-				setmetatable(dest, mt)
-			end
-		elseif type(v) == "table" then
-			if not dest[k] then dest[k] = {} end
-			copyDefaults(dest[k], v, force)
-		else
-			if (dest[k] == nil) or force then
-				dest[k] = v
-			end
-		end
-	end
-end
-
-local function removeDefaults(db, defaults)
-	if not db then return end
-	for k,v in pairs(defaults) do
-		if k == "*" and type(v) == "table" then
-			-- check for any defaults that have been changed
-			local mt = getmetatable(db)
-			local cache = rawget(mt, "__cache")
-
-			for cacheKey,cacheValue in pairs(cache) do
-				removeDefaults(cacheValue, v)
-				if next(cacheValue) ~= nil then
-					-- Something's changed
-					rawset(db, cacheKey, cacheValue)
-				end
-			end
-			-- Now loop through all the actual k,v pairs and remove
-			for key,value in pairs(db) do
-				removeDefaults(value, v)
-			end
-		elseif type(v) == "table" and db[k] then
-			removeDefaults(db[k], v)
-			if not next(db[k]) then
-				db[k] = nil
-			end
-		else
-			if db[k] == defaults[k] then
-				db[k] = nil
-			end
-		end
-	end
-end
-
-local function initSection(db, section, svstore, key, defaults)
-	local sv = rawget(db, "sv")
-
-	local tableCreated
-	if not sv[svstore] then sv[svstore] = {} end
-	if not sv[svstore][key] then
-		sv[svstore][key] = {}
-		tableCreated = true
-	end
-
-	local tbl = sv[svstore][key]
-
-	if defaults then
-		copyDefaults(tbl, defaults)
-	end
-	rawset(db, section, tbl)
-
-	return tableCreated, tbl
-end
-
-local dbmt = {
-	__index = function(t, section)
-		local keys = rawget(t, "keys")
-		local key = keys[section]
-		if key then
-			local defaultTbl = rawget(t, "defaults")
-			local defaults = defaultTbl and defaultTbl[section]
-
-			if section == "profile" then
-				local new = initSection(t, section, "profiles", key, defaults)
-				if new then
-					Dongle:TriggerMessage("DONGLE_PROFILE_CREATED", t, rawget(t, "parent"), rawget(t, "sv_name"), key)
-				end
-			elseif section == "profiles" then
-				local sv = rawget(t, "sv")
-				if not sv.profiles then sv.profiles = {} end
-				rawset(t, "profiles", sv.profiles)
-			elseif section == "global" then
-				local sv = rawget(t, "sv")
-				if not sv.global then sv.global = {} end
-				if defaults then
-					copyDefaults(sv.global, defaults)
-				end
-				rawset(t, section, sv.global)
-			else
-				initSection(t, section, section, key, defaults)
-			end
-		end
-
-		return rawget(t, section)
-	end
-}
-
-local function initdb(parent, name, defaults, defaultProfile, olddb)
-	-- This allows us to use an arbitrary table as base instead of saved variable name
-	local sv
-	if type(name) == "string" then
-		sv = getglobal(name)
-		if not sv then
-			sv = {}
-			setglobal(name, sv)
-		end
-	elseif type(name) == "table" then
-		sv = name
-	end
-
-	-- Generate the database keys for each section
-	local char = string.format("%s - %s", UnitName("player"), GetRealmName())
-	local realm = GetRealmName()
-	local class = select(2, UnitClass("player"))
-	local race = select(2, UnitRace("player"))
-	local faction = UnitFactionGroup("player")
-	local factionrealm = string.format("%s - %s", faction, realm)
-
-	-- Make a container for profile keys
-	if not sv.profileKeys then sv.profileKeys = {} end
-
-	-- Try to get the profile selected from the char db
-	local profileKey = sv.profileKeys[char] or defaultProfile or char
-	sv.profileKeys[char] = profileKey
-
-	local keyTbl= {
-		["char"] = char,
-		["realm"] = realm,
-		["class"] = class,
-		["race"] = race,
-		["faction"] = faction,
-		["factionrealm"] = factionrealm,
-		["global"] = true,
-		["profile"] = profileKey,
-		["profiles"] = true, -- Don't create until we need
-	}
-
-	-- If we've been passed an old database, clear it out
-	if olddb then
-		for k,v in pairs(olddb) do olddb[k] = nil end
-	end
-
-	-- Give this database the metatable so it initializes dynamically
-	local db = setmetatable(olddb or {}, dbmt)
-
-	-- Copy methods locally
-	for idx,method in pairs(dbMethods) do
-		db[method] = Dongle[method]
-	end
-
-	-- Set some properties in the object we're returning
-	db.profiles = sv.profiles
-	db.keys = keyTbl
-	db.sv = sv
-	db.sv_name = name
-	db.defaults = defaults
-	db.parent = parent
-
-	databases[db] = true
-
-	return db
-end
-
-function Dongle:InitializeDB(name, defaults, defaultProfile)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeDB"))
-	argcheck(name, 2, "string", "table")
-	argcheck(defaults, 3, "table", "nil")
-	argcheck(defaultProfile, 4, "string", "nil")
-
-	return initdb(self, name, defaults, defaultProfile)
-end
-
--- This function operates on a Dongle DB object
-function Dongle.RegisterDefaults(db, defaults)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterDefaults"))
-	assert(3, db.defaults == nil, L["REPLACE_DEFAUTS"])
-	argcheck(defaults, 2, "table")
-
-	for section,key in pairs(db.keys) do
-		if defaults[section] and rawget(db, section) then
-			copyDefaults(db[section], defaults[section])
-		end
-	end
-
-	db.defaults = defaults
-end
-
-function Dongle:ClearDBDefaults()
-	for db in pairs(databases) do
-		local defaults = db.defaults
-		local sv = db.sv
-
-		if db and defaults then
-			for section,key in pairs(db.keys) do
-				if defaults[section] and rawget(db, section) then
-					removeDefaults(db[section], defaults[section])
-				end
-			end
-
-			for section,key in pairs(db.keys) do
-				local tbl = rawget(db, section)
-				if tbl and not next(tbl) then
-					if sv[section] then
-						if type(key) == "string" then
-							sv[section][key] = nil
-						else
-							sv[section] = nil
-						end
-					end
-				end
-			end
-		end
-	end
-end
-
-function Dongle.SetProfile(db, name)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "SetProfile"))
-	argcheck(name, 2, "string")
-
-	local old = db.profile
-	local defaults = db.defaults and db.defaults.profile
-
-	if defaults then
-		-- Remove the defaults from the old profile
-		removeDefaults(old, defaults)
-	end
-
-	db.profile = nil
-	db.keys["profile"] = name
-	db.sv.profileKeys[db.keys.char] = name
-
-	Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile)
-end
-
-function Dongle.GetProfiles(db, tbl)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetProfiles"))
-	argcheck(tbl, 2, "table", "nil")
-
-	-- Clear the container table
-	if tbl then
-		for k,v in pairs(tbl) do tbl[k] = nil end
-	else
-		tbl = {}
-	end
-
-	local i = 0
-	for profileKey in pairs(db.profiles) do
-		i = i + 1
-		tbl[i] = profileKey
-	end
-
-	-- Add the current profile, if it hasn't been created yet
-	if rawget(db, "profile") == nil then
-		i = i + 1
-		tbl[i] = db.keys.profile
-	end
-
-	return tbl, i
-end
-
-function Dongle.GetCurrentProfile(db)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "GetCurrentProfile"))
-	return db.keys.profile
-end
-
-function Dongle.DeleteProfile(db, name)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "DeleteProfile"))
-	argcheck(name, 2, "string")
-
-	if db.keys.profile == name then
-		error(L["CANNOT_DELETE_ACTIVE_PROFILE"], 2)
-	end
-
-	assert(type(db.sv.profiles[name]) == "table", L["DELETE_NONEXISTANT_PROFILE"])
-
-	db.sv.profiles[name] = nil
-	Dongle:TriggerMessage("DONGLE_PROFILE_DELETED", db, db.parent, db.sv_name, name)
-end
-
-function Dongle.CopyProfile(db, name)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "CopyProfile"))
-	argcheck(name, 2, "string")
-
-	assert(3, db.keys.profile ~= name, L["SAME_SOURCE_DEST"])
-	assert(3, type(db.sv.profiles[name]) == "table", string.format(L["PROFILE_DOES_NOT_EXIST"], name))
-
-	local profile = db.profile
-	local source = db.sv.profiles[name]
-
-	copyDefaults(profile, source, true)
-	Dongle:TriggerMessage("DONGLE_PROFILE_COPIED", db, db.parent, db.sv_name, name, db.keys.profile)
-end
-
-function Dongle.ResetProfile(db)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetProfile"))
-
-	local profile = db.profile
-
-	for k,v in pairs(profile) do
-		profile[k] = nil
-	end
-
-	local defaults = db.defaults and db.defaults.profile
-	if defaults then
-		copyDefaults(profile, defaults)
-	end
-	Dongle:TriggerMessage("DONGLE_PROFILE_RESET", db, db.parent, db.sv_name, db.keys.profile)
-end
-
-
-function Dongle.ResetDB(db, defaultProfile)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "ResetDB"))
-		argcheck(defaultProfile, 2, "nil", "string")
-
-	local sv = db.sv
-	for k,v in pairs(sv) do
-		sv[k] = nil
-	end
-
-	local parent = db.parent
-
-	initdb(parent, db.sv_name, db.defaults, defaultProfile, db)
-	Dongle:TriggerMessage("DONGLE_DATABASE_RESET", db, parent, db.sv_name, db.keys.profile)
-	Dongle:TriggerMessage("DONGLE_PROFILE_CHANGED", db, db.parent, db.sv_name, db.keys.profile)
-	return db
-end
-
-function Dongle.RegisterNamespace(db, name, defaults)
-	assert(3, databases[db], string.format(L["MUST_CALLFROM_DBOBJECT"], "RegisterNamespace"))
-	argcheck(name, 2, "string")
-	argcheck(defaults, 3, "nil", "table")
-
-	local sv = db.sv
-	if not sv.namespaces then sv.namespaces = {} end
-	if not sv.namespaces[name] then
-		sv.namespaces[name] = {}
-	end
-
-	local newDB = initdb(db, sv.namespaces[name], defaults, db.keys.profile)
-	-- Remove the :SetProfile method from newDB
-	newDB.SetProfile = nil
-
-	if not db.children then db.children = {} end
-	table.insert(db.children, newDB)
-	return newDB
-end
-
---[[-------------------------------------------------------------------------
-	Slash Command System
----------------------------------------------------------------------------]]
-
-local slashCmdMethods = {
-	"InjectDBCommands",
-	"RegisterSlashHandler",
-	"PrintUsage",
-}
-
-local function OnSlashCommand(cmd, cmd_line)
-	if cmd.patterns then
-		for idx,tbl in pairs(cmd.patterns) do
-			local pattern = tbl.pattern
-			if string.match(cmd_line, pattern) then
-				local handler = tbl.handler
-				if type(tbl.handler) == "string" then
-					local obj
-					-- Look in the command object before we look at the parent object
-					if cmd[handler] then obj = cmd end
-					if cmd.parent[handler] then obj = cmd.parent end
-					if obj then
-						obj[handler](obj, string.match(cmd_line, pattern))
-					end
-				else
-					handler(string.match(cmd_line, pattern))
-				end
-				return
-			end
-		end
-	end
-	cmd:PrintUsage()
-end
-
-function Dongle:InitializeSlashCommand(desc, name, ...)
-	local reg = lookup[self]
-	assert(3, reg, string.format(L["MUST_CALLFROM_REGISTERED"], "InitializeSlashCommand"))
-	argcheck(desc, 2, "string")
-	argcheck(name, 3, "string")
-	argcheck(select(1, ...), 4, "string")
-	for i = 2,select("#", ...) do
-		argcheck(select(i, ...), i+2, "string")
-	end
-
-	local cmd = {}
-	cmd.desc = desc
-	cmd.name = name
-	cmd.parent = self
-	cmd.slashes = { ... }
-	for idx,method in pairs(slashCmdMethods) do
-		cmd[method] = Dongle[method]
-	end
-
-	local genv = getfenv(0)
-
-	for i = 1,select("#", ...) do
-		genv["SLASH_"..name..tostring(i)] = "/"..select(i, ...)
-	end
-
-	genv.SlashCmdList[name] = function(...) OnSlashCommand(cmd, ...) end
-
-	commands[cmd] = true
-
-	return cmd
-end
-
-function Dongle.RegisterSlashHandler(cmd, desc, pattern, handler)
-	assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "RegisterSlashHandler"))
-
-	argcheck(desc, 2, "string")
-	argcheck(pattern, 3, "string")
-	argcheck(handler, 4, "function", "string")
-
-	if not cmd.patterns then
-		cmd.patterns = {}
-	end
-
-	table.insert(cmd.patterns, {
-		["desc"] = desc,
-		["handler"] = handler,
-		["pattern"] = pattern,
-	})
-end
-
-function Dongle.PrintUsage(cmd)
-	assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "PrintUsage"))
-	local parent = cmd.parent
-
-	parent:Echo(cmd.desc.."\n".."/"..table.concat(cmd.slashes, ", /")..":\n")
-	if cmd.patterns then
-		for idx,tbl in ipairs(cmd.patterns) do
-			parent:Echo(" - " .. tbl.desc)
-		end
-	end
-end
-
-local dbcommands = {
-	["copy"] = {
-		L["DBSLASH_PROFILE_COPY_DESC"],
-		L["DBSLASH_PROFILE_COPY_PATTERN"],
-		"CopyProfile",
-	},
-	["delete"] = {
-		L["DBSLASH_PROFILE_DELETE_DESC"],
-		L["DBSLASH_PROFILE_DELETE_PATTERN"],
-		"DeleteProfile",
-	},
-	["list"] = {
-		L["DBSLASH_PROFILE_LIST_DESC"],
-		L["DBSLASH_PROFILE_LIST_PATTERN"],
-	},
-	["reset"] = {
-		L["DBSLASH_PROFILE_RESET_DESC"],
-		L["DBSLASH_PROFILE_RESET_PATTERN"],
-		"ResetProfile",
-	},
-	["set"] = {
-		L["DBSLASH_PROFILE_SET_DESC"],
-		L["DBSLASH_PROFILE_SET_PATTERN"],
-		"SetProfile",
-	},
-}
-
-function Dongle.InjectDBCommands(cmd, db, ...)
-	assert(3, commands[cmd], string.format(L["MUST_CALLFROM_SLASH"], "InjectDBCommands"))
-	assert(3, databases[db], string.format(L["BAD_ARGUMENT_DB"], 2, "InjectDBCommands"))
-	local argc = select("#", ...)
-	assert(3, argc > 0, L["INJECTDB_USAGE"])
-
-	for i=1,argc do
-		local cmdname = string.lower(select(i, ...))
-		local entry = dbcommands[cmdname]
-		assert(entry, L["INJECTDB_USAGE"])
-		local func = entry[3]
-
-		local handler
-		if cmdname == "list" then
-			handler = function(...)
-				local profiles = db:GetProfiles()
-				db.parent:Print(L["DBSLASH_PROFILE_LIST_OUT"] .. "\n" .. strjoin("\n", unpack(profiles)))
-			end
-		else
-			handler = function(...) db[entry[3]](db, ...) end
-		end
-
-		cmd:RegisterSlashHandler(entry[1], entry[2], handler)
-	end
-end
-
---[[-------------------------------------------------------------------------
-	Internal Message/Event Handlers
----------------------------------------------------------------------------]]
-
-local function PLAYER_LOGOUT(event)
-	Dongle:ClearDBDefaults()
-	for k,v in pairs(registry) do
-		local obj = v.obj
-		if type(obj["Disable"]) == "function" then
-			safecall(obj["Disable"], obj)
-		end
-	end
-end
-
-local PLAYER_LOGIN
-do
-	local lockPlayerLogin = false
-
-	function PLAYER_LOGIN()
-		if lockPlayerLogin then return end
-
-		lockPlayerLogin = true
-
-		local obj = table.remove(loadorder, 1)
-		while obj do
-			if type(obj.Enable) == "function" then
-				safecall(obj.Enable, obj)
-			end
-			obj = table.remove(loadorder, 1)
-		end
-
-		lockPlayerLogin = false
-	end
-end
-
-local function ADDON_LOADED(event, ...)
-	local obj = table.remove(loadqueue, 1)
-	while obj do
-		table.insert(loadorder, obj)
-
-		if type(obj.Initialize) == "function" then
-			safecall(obj.Initialize, obj)
-		end
-
-		obj = table.remove(loadqueue, 1)
-	end
-
-	if IsLoggedIn() then
-		PLAYER_LOGIN()
-	end
-end
-
-local function DONGLE_PROFILE_CHANGED(msg, db, parent, sv_name, profileKey)
-	local children = db.children
-	if children then
-		for i,namespace in ipairs(children) do
-			local old = namespace.profile
-			local defaults = namespace.defaults and namespace.defaults.profile
-
-			if defaults then
-				-- Remove the defaults from the old profile
-				removeDefaults(old, defaults)
-			end
-
-			namespace.profile = nil
-			namespace.keys["profile"] = profileKey
-		end
-	end
-end
-
---[[-------------------------------------------------------------------------
-	DongleStub required functions and registration
----------------------------------------------------------------------------]]
-
-function Dongle:GetVersion() return major,minor end
-
-local function Activate(self, old)
-	if old then
-		registry = old.registry or registry
-		lookup = old.lookup or lookup
-		loadqueue = old.loadqueue or loadqueue
-		loadorder = old.loadorder or loadorder
-		events = old.events or events
-		databases = old.databases or databases
-		commands = old.commands or commands
-		messages = old.messages or messages
-		frame = old.frame or CreateFrame("Frame")
-		timers = old.timers or timers
-		heap = old.heap or heap
-	else
-		frame = CreateFrame("Frame")
-		local reg = {obj = self, name = "Dongle"}
-		registry[major] = reg
-		lookup[self] = reg
-		lookup[major] = reg
-	end
-
-	self.registry = registry
-	self.lookup = lookup
-	self.loadqueue = loadqueue
-	self.loadorder = loadorder
-	self.events = events
-	self.databases = databases
-	self.commands = commands
-	self.messages = messages
-	self.frame = frame
-	self.timers = timers
-	self.heap = heap
-
-	frame:SetScript("OnEvent", OnEvent)
-	frame:SetScript("OnUpdate", OnUpdate)
-
-	-- Lets ensure the lookup table has our entry
-	-- This fixes an issue with upgrades
-	lookup[self] = lookup[major]
-
-	-- Register for events using Dongle itself
-	self:RegisterEvent("ADDON_LOADED", ADDON_LOADED)
-	self:RegisterEvent("PLAYER_LOGIN", PLAYER_LOGIN)
-	self:RegisterEvent("PLAYER_LOGOUT", PLAYER_LOGOUT)
-	self:RegisterMessage("DONGLE_PROFILE_CHANGED", DONGLE_PROFILE_CHANGED)
-
-	-- Convert all the modules handles
-	for name,obj in pairs(registry) do
-		for k,v in ipairs(methods) do
-			obj[k] = self[v]
-		end
-	end
-
-	-- Convert all database methods
-	for db in pairs(databases) do
-		for idx,method in ipairs(dbMethods) do
-			db[method] = self[method]
-		end
-	end
-
-	-- Convert all slash command methods
-	for cmd in pairs(commands) do
-		for idx,method in ipairs(slashCmdMethods) do
-			cmd[method] = self[method]
-		end
-	end
-end
-
-Dongle = DongleStub:Register(Dongle, Activate)
diff --git a/LICENSE b/LICENSE
old mode 100644
new mode 100755
index 23d8160..bac1234
--- a/LICENSE
+++ b/LICENSE
@@ -1,5 +1,5 @@
 --[[-------------------------------------------------------------------------
-  Copyright (c) 2006-2007, James N. Whitehead II
+  Copyright (c) 2006-2010, James N. Whitehead II
   All rights reserved.

   Redistribution and use in source and binary forms, with or without
diff --git a/Localization.deDE.lua b/Localization.deDE.lua
deleted file mode 100644
index 9d72b38..0000000
--- a/Localization.deDE.lua
+++ /dev/null
@@ -1,110 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for deDE.  Any commented lines need to be updated
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
-if GetLocale() == "deDE" then
-	L.RANK                    = "Rang"
-	L.RANK_PATTERN            = "Rang (%d+)"
-	L.CAST_FORMAT             = "%s(Rang %s)"
-
-	L.RACIAL_PASSIVE          = "Volk Passiv"
-	L.PASSIVE                 = SPELL_PASSIVE
-
-	L.CLICKSET_DEFAULT        = "Standard"
-	L.CLICKSET_HARMFUL        = "Schadhafte Aktionen"
-	L.CLICKSET_HELPFUL        = "Hilfreiche Aktionen"
-	L.CLICKSET_OOC            = "Au\195\159erhalb des Kampfes"
-	L.CLICKSET_BEARFORM       = "B\195\164ren Gestalt"
-	L.CLICKSET_CATFORM        = "Katzen Gestalt"
-    L.CLICKSET_AQUATICFORM    = "Wasser Gestalt"
-	L.CLICKSET_TRAVELFORM     = "Reise Gestalt"
-	L.CLICKSET_MOONKINFORM    = "Moonkin Gestalt"
-	L.CLICKSET_TREEOFLIFE     = "Baum des Lebens Gestalt"
-	L.CLICKSET_SHADOWFORM     = "Schattengestalt"
-	L.CLICKSET_STEALTHED      = "Getarnt"
-	L.CLICKSET_BATTLESTANCE   = "Kampfhaltung"
-	L.CLICKSET_DEFENSIVESTANCE = "Verteidigungshaltung"
-	L.CLICKSET_BERSERKERSTANCE = "Berserkerhaltung"
-
-	L.BEAR_FORM = "B\195\164ren Gestalt"
-	L.DIRE_BEAR_FORM = "Dire B\195\164ren Gestalt"
-	L.CAT_FORM = "Katzen Gestalt"
-	L.AQUATIC_FORM = "Wasser Gestalt"
-	L.TRAVEL_FORM = "Reise Gestalt"
-	L.TREEOFLIFE = "Baum des Lebens"
-	L.MOONKIN_FORM = "oonkin Gestalt"
-	L.STEALTH = "Tarnen"
-	L.SHADOWFORM = "Schattengestalt"
-	L.BATTLESTANCE = "Kampfhaltung"
-	L.DEFENSIVESTANCE = "Verteidigungshaltung"
-	L.BERSERKERSTANCE = "Berserkerhaltung"
-
-	L.BINDING_NOT_DEFINED     = "Belegung nicht definiert."
-
-	L.CANNOT_CHANGE_COMBAT    = "\195\132nderungen w\195\164rend des Kampfes nicht m\195\182glich. \195\132nderungen werden bis Kampfende verschoben."
-	L.APPLY_QUEUE             = "Au\195\159erhalb des Kampfes. Ausstehende \195\132nderungen werden durchgef\195\188hrt."
-	L.PROFILE_CHANGED         = "Profil gewechselt zu '%s'."
-	L.PROFILE_DELETED         = "Profile '%s' wurde gel\195\182scht."
-
-	L.ACTION_ACTIONBAR = "Wechsel Aktionsleiste"
-	L.ACTION_ACTION = "Aktion Taste"
-	L.ACTION_PET = "Tier Aktion Taste"
-	L.ACTION_SPELL = "Zauber ausf\195\188hren"
-	L.ACTION_ITEM = "Benutze Gegenstand"
-	L.ACTION_MACRO = "Starte eigenes Makro"
-	L.ACTION_STOP = "Zauber Unterbrechen"
-	L.ACTION_TARGET = "Ziel ausw\195\164hlen"
-	L.ACTION_FOCUS = "Setze Fokus"
-	L.ACTION_ASSIST = "Assistiere Einheit"
-	L.ACTION_CLICK = "Klicke Taste"
-	L.ACTION_MENU = "Zeige Men\195\188"
-
-	L.HELP_TEXT               = "Willkomen zu Clique. Zuerst suchst du einfach im Zauberbuch einen Spruch aus, welchen du an eine Taste binden m\195\182chtest. Dann klickst du mit der entsprechenden Taste diesen Zauber an. Als Beispiel, gehe zu \"Blitzheilung\" und klicke darauf mit Shift+LinkeMaustaste um diesen Zauber auf Shift+LinkeMaustaste zu legen."
-	L.CUSTOM_HELP             = "Dies ist das Clique konfigurations Fenster. Von hier aus kannst du alle Klick-Kombinationen, die uns die UI erlaubt, konfigurieren. W\195\164hle eine Grundfunktion aus der linken Spalte. Klicke dann auf den unteren Button mit einer Tastenkombination deiner Wahl, und erg\195\164nze (falls ben\195\182tigt) die Parameter."
-
-	L.BS_ACTIONBAR_HELP = "Wechselt die Aktionsleiste. 'increment' Wechselt eine Leiste rauf, 'decrement' mach das Gegenteil. Gibst du eine Zahl ein, wird nur die enstprechende Leiste gezeigt. Die Eingabe von 1,3 w\195\188rde zwischen Leiste 1 und 3 wechseln"
-	L.BS_ACTIONBAR_ARG1_LABEL = "Aktion:"
-
-	L.BS_ACTION_HELP = "Simuliert einen Klick auf eine Aktions Taste. Gebe die Nummer der Aktions Taste an."
-	L.BS_ACTION_ARG1_LABEL = "Tasten Nummer:"
-	L.BS_ACTION_ARG2_LABEL = "(Optional) Unit:"
-
-	L.BS_PET_HELP = "Simuliert einen Klick auf eine Tier Aktions Taste. Gebe die Nummer der Aktions Taste an."
-	L.BS_PET_ARG1_LABEL = "Tier Tasten Nummer:"
-	L.BS_PET_ARG2_LABEL = "(Optional) Unit:"
-
-	L.BS_SPELL_HELP = "F\195\188hrt einen Zauber aus dem Zauberbuch aus. M\195\182glich sind Zaubername, und optional Rucksack/Platz, oder Gegenstand-Name als Ziel eines Zaubers.(zB. Tier f\195\188ttern)"
-	L.BS_SPELL_ARG1_LABEL = "Zauber Name:"
-	L.BS_SPELL_ARG2_LABEL = "*Rang/Rucksack Nr:"
-	L.BS_SPELL_ARG3_LABEL = "*Platz Nummer:"
-	L.BS_SPELL_ARG4_LABEL = "*Gegenstands Name:"
-	L.BS_SPELL_ARG5_LABEL = "(Optional) Unit:"
-
-	L.BS_ITEM_HELP = "Benutze Gegenstand. Angabe von Rucksack/Platz, oder Gegenstand-Name."
-	L.BS_ITEM_ARG1_LABEL = "Rucksack Nummer:"
-	L.BS_ITEM_ARG2_LABEL = "Platz Nummer:"
-	L.BS_ITEM_ARG3_LABEL = "Gegenstands Name:"
-	L.BS_ITEM_ARG4_LABEL = "(Optional) Unit:"
-
-	L.BS_MACRO_HELP = "Benutzt eigenes Makro aus vorhandenem index"
-	L.BS_ARG1_LABEL = "Makro Index:"
-	L.BS_ARG2_LABEL = "Makro Text:"
-
-	L.BS_STOP_HELP = "Unterbricht den laufenden Zauberspruch"
-
-	L.BS_TARGET_HELP = "Visiert \"unit\" als Ziel an"
-	L.BS_TARGET_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_FOCUS_HELP = "W\195\164hlt \"focus\" Einheit aus."
-	L.BS_FOCUS_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_ASSIST_HELP = "Assistiert der Einheit \"unit\""
-	L.BS_ASSIST_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_CLICK_HELP = "Simuliert Klick auf eine Aktions Taste"
-	L.BS_CLICK_ARG1_LABEL = "Tasten Name:"
-
-	L.BS_MENU_HELP = "Zeigt das Einheiten Pop-Up Men\195\188"
-end
\ No newline at end of file
diff --git a/Localization.enUS.lua b/Localization.enUS.lua
index f2519a4..85879d1 100644
--- a/Localization.enUS.lua
+++ b/Localization.enUS.lua
@@ -1,124 +1 @@
---[[---------------------------------------------------------------------------------
-    Localisation for English
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
--- This is the default locale.
-if GetLocale() then
-	L.RANK                    = "Rank"
-	L.RANK_PATTERN            = "Rank (%d+)"
-	L.CAST_FORMAT             = "%s(Rank %s)"
-
-	L.RACIAL_PASSIVE          = "Racial Passive"
-	L.PASSIVE                 = SPELL_PASSIVE
-
-	L.CLICKSET_DEFAULT        = "Default"
-	L.CLICKSET_HARMFUL        = "Harmful actions"
-	L.CLICKSET_HELPFUL        = "Helpful actions"
-	L.CLICKSET_OOC            = "Out of Combat"
-	L.CLICKSET_BEARFORM       = "Bear Form"
-	L.CLICKSET_CATFORM        = "Cat Form"
-    L.CLICKSET_AQUATICFORM    = "Aquatic Form"
-	L.CLICKSET_TRAVELFORM     = "Travel Form"
-	L.CLICKSET_MOONKINFORM    = "Moonkin Form"
-	L.CLICKSET_TREEOFLIFE     = "Tree of Life Form"
-	L.CLICKSET_SHADOWFORM     = "Shadowform"
-	L.CLICKSET_STEALTHED      = "Stealthed"
-	L.CLICKSET_BATTLESTANCE   = "Battle Stance"
-	L.CLICKSET_DEFENSIVESTANCE = "Defensive Stance"
-	L.CLICKSET_BERSERKERSTANCE = "Berserker Stance"
-
-	L.BEAR_FORM = "Bear Form"
-	L.DIRE_BEAR_FORM = "Dire Bear Form"
-	L.CAT_FORM = "Cat Form"
-	L.AQUATIC_FORM = "Aquatic Form"
-	L.TRAVEL_FORM = "Travel Form"
-	L.TREEOFLIFE = "Tree of Life"
-	L.MOONKIN_FORM = "Moonkin Form"
-	L.STEALTH = "Stealth"
-	L.SHADOWFORM = "Shadowform"
-	L.BATTLESTANCE = "Battle Stance"
-	L.DEFENSIVESTANCE = "Defensive Stance"
-	L.BERSERKERSTANCE = "Berserker Stance"
-
-	L.BINDING_NOT_DEFINED     = "Binding not defined"
-	L.CANNOT_CHANGE_COMBAT    = "Cannot make changes in combat.  These changes will be delayed until you exit combat."
-	L.APPLY_QUEUE             = "Out of combat.  Applying all queued changes."
-	L.PROFILE_CHANGED         = "Profile has changed to '%s'."
-	L.PROFILE_DELETED         = "Profile '%s' has been deleted."
-	L.PROFILE_RESET         = "Your profile '%s' has been reset."
-
-	L.ACTION_ACTIONBAR = "Change ActionBar"
-	L.ACTION_ACTION = "Action Button"
-	L.ACTION_PET = "Pet Action Button"
-	L.ACTION_SPELL = "Cast Spell"
-	L.ACTION_ITEM = "Use Item"
-	L.ACTION_MACRO = "Run Custom Macro"
-	L.ACTION_STOP = "Stop Casting"
-	L.ACTION_TARGET = "Target Unit"
-	L.ACTION_FOCUS = "Set Focus"
-	L.ACTION_ASSIST = "Assist Unit"
-	L.ACTION_CLICK = "Click Button"
-	L.ACTION_MENU = "Show Menu"
-
-	L.HELP_TEXT               = "Welcome to Clique.  For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click.  Then click on that spell with whatever click-binding you would like.  For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick."
-	L.CUSTOM_HELP             = "This is the Clique custom edit screen.  From here you can configure any of the combinations that the UI makes available to us in response to clicks.  Select a base action from the left column.  You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)."
-
-	L.BS_ACTIONBAR_HELP = "Change the actionbar.  'increment' will move it up one page, 'decrement' does the opposite.  If you supply a number, the action bar will be turned to that page.  You can specify 1,3 to toggle between pages 1 and 3"
-	L.BS_ACTIONBAR_ARG1_LABEL = "Action:"
-
-	L.BS_ACTION_HELP = "Simulate a click on an action button.  Specify the number of the action button."
-	L.BS_ACTION_ARG1_LABEL = "Button Number:"
-	L.BS_ACTION_ARG2_LABEL = "(Optional) Unit:"
-
-	L.BS_PET_HELP = "Simulate a click on an pet's action button.  Specify the number of the button."
-	L.BS_PET_ARG1_LABEL = "Pet Button Number:"
-	L.BS_PET_ARG2_LABEL = "(Optional) Unit:"
-
-	L.BS_SPELL_HELP = "Cast a spell from the spellbook.  Takes a spell name, and optionally a bag and slot, or item name to use as the target of the spell (i.e. Feed Pet)"
-	L.BS_SPELL_ARG1_LABEL = "Spell Name:"
-	L.BS_SPELL_ARG2_LABEL = "*Rank/Bag Number:"
-	L.BS_SPELL_ARG3_LABEL = "*Slot Number:"
-	L.BS_SPELL_ARG4_LABEL = "*Item Name:"
-	L.BS_SPELL_ARG5_LABEL = "(Optional) Unit:"
-
-	L.BS_ITEM_HELP = "Use an item.  Can take either a bag and slot, or an item name."
-	L.BS_ITEM_ARG1_LABEL = "Bag Number:"
-	L.BS_ITEM_ARG2_LABEL = "Slot Number:"
-	L.BS_ITEM_ARG3_LABEL = "Item Name:"
-	L.BS_ITEM_ARG4_LABEL = "(Optional) Unit:"
-
-	L.BS_MACRO_HELP = "Use a custom macro in a given index"
-	L.BS_MACRO_ARG1_LABEL = "Macro Index:"
-	L.BS_MACRO_ARG2_LABEL = "Macro Text:"
-
-	L.BS_STOP_HELP = "Stops casting the current spell"
-
-	L.BS_TARGET_HELP = "Targets the unit"
-	L.BS_TARGET_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_FOCUS_HELP = "Sets your \"focus\" unit"
-	L.BS_FOCUS_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_ASSIST_HELP = "Assists the unit"
-	L.BS_ASSIST_ARG1_LABEL = "(Optional) Unit:"
-
-	L.BS_CLICK_HELP = "Simulate click on a button"
-	L.BS_CLICK_ARG1_LABEL = "Button Name:"
-
-	L.BS_MENU_HELP = "Shows the unit pop up menu"
-
-    L.CUSTOM = "Custom"
-    L.FRAMES = "Frames"
-    L.PROFILES = "Profiles"
-    L.DELETE = "Delete"
-    L.EDIT = "Edit"
-
-    L["Clique Options"] = "Clique Options"
-    L["Primary:"] = "Primary:"
-    L["Secondary:"] = "Secondary:"
-    L.DOWNCLICK_LABEL = "Trigger clicks on the 'down' portion of the click"
-    L.SPECSWITCH_LABEL = "Change profile when switching talent specs"
-end
-
+-- This localization file is empty, and will be automatically generated
diff --git a/Localization.esES.lua b/Localization.esES.lua
deleted file mode 100644
index d896357..0000000
--- a/Localization.esES.lua
+++ /dev/null
@@ -1,110 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for esES (Spanish)
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
--- This is the default locale.
-if GetLocale() == "esES" then
-	L.RANK                    = "Rango"
-	L.RANK_PATTERN            = "Rango (%d+)"
-	L.CAST_FORMAT             = "%s(Rango %s)"
-	L.RACIAL_PASSIVE          = "Pasivo racial"
-	L.PASSIVE                 = SPELL_PASSIVE
-	L.CLICKSET_DEFAULT        = "Por Defecto"
-	L.CLICKSET_HARMFUL        = "Aciones de Daño"
-	L.CLICKSET_HELPFUL        = "Acciones de Ayuda"
-	L.CLICKSET_OOC            = "Fuera de Combate"
-	L.CLICKSET_BEARFORM       = "Forma de Oso"
-	L.CLICKSET_CATFORM        = "Forma Felina"
-    L.CLICKSET_AQUATICFORM    = "Forma Acuática"
-	L.CLICKSET_TRAVELFORM     = "Forma de Viaje"
-	L.CLICKSET_MOONKINFORM    = "Forma de Moonkin"
-	L.CLICKSET_TREEOFLIFE     = "Forma de Arbol de Vida"
-	L.CLICKSET_SHADOWFORM     = "Forma de las Sombras"
-	L.CLICKSET_STEALTHED      = "En Sigilo"
-	L.CLICKSET_BATTLESTANCE   = "Actitud de Batalla"
-	L.CLICKSET_DEFENSIVESTANCE = "Actitud Defensiva"
-	L.CLICKSET_BERSERKERSTANCE = "Actitud Rabiosa"
-
-	L.BEAR_FORM = "Forma de Oso"
-	L.DIRE_BEAR_FORM = "Forma de Oso Temible"
-	L.CAT_FORM = "Forma Felina"
-	L.AQUATIC_FORM = "Forma Acuática"
-	L.TRAVEL_FORM = "Forma de Viaje"
-	L.TREEOFLIFE = "Arbol de Vida"
-	L.MOONKIN_FORM = "Forma de Moonkin"
-	L.STEALTH = "Sigilo"
-	L.SHADOWFORM = "Forma de las Sombras"
-	L.BATTLESTANCE = "Actitud de Batalla"
-	L.DEFENSIVESTANCE = "Actitud Defensiva"
-	L.BERSERKERSTANCE = "Actitud Rabiosa"
-
-	L.BINDING_NOT_DEFINED     = "Binding no definido"
-	L.CANNOT_CHANGE_COMBAT    = "No puede hacer cambios en combate.  Los cambios se realizarán cuando salga de combate."
-	L.APPLY_QUEUE             = "Fuera de combate.  Aplicando todos los cambios pendientes."
-	L.PROFILE_CHANGED         = "El perfil ha cambiado a '%s'."
-	L.PROFILE_DELETED         = "El perfil '%s' ha sido borrado."
-	L.PROFILE_RESET         = "Su perfil '%s' ha sido resetiado."
-	L.ACTION_ACTIONBAR = "Cambiar Barra de Acción"
-	L.ACTION_ACTION = "Botón de Acción"
-	L.ACTION_PET = "Botón de Acción Mascota"
-	L.ACTION_SPELL = "Lanzar Hechizo"
-	L.ACTION_ITEM = "Usar Item"
-	L.ACTION_MACRO = "Ejecutar macro por defecto"
-	L.ACTION_STOP = "Parar Lanzamiento"
-	L.ACTION_TARGET = "Apuntar Unidad"
-	L.ACTION_FOCUS = "Fijar Foco"
-	L.ACTION_ASSIST = "Asistir Unidad"
-	L.ACTION_CLICK = "Click Botón"
-	L.ACTION_MENU = "Mostrar Menú"
-
-	L.HELP_TEXT               = "Bienvenido a Clique.  Para su operación básica, usted puede navegar por el Libro de Hechizos (Spellbook) y determinar que hechizo le gustaría asociar a un click específico.  Luego haga click sobre el hechizo, con la combinación de teclas que a usted le guste (click o click + teclado).  Por ejemplo, un sacerdote puede buscar en su libro de hechizos la \"Sanación Relámpago\" (Flash Heal) y realizar sobre este un Shift+LeftClick, lo cual asociará dicho hechizo a las combinación de teclas Shift+LeftClick."
-	L.CUSTOM_HELP             = "Esta es la pantalla de edición por defecto de Clique.  Desde aquí usted puede configurar cualquiera de las combinaciones que el UI tiene disponible para nosotros en respuesta a los clicks.  Seleccione una acción base de la columna izquierda.  Usted puede hacer clic en el botón de abajo para asociar las convinaciones de clicks + teclas que desee, y entonces proporcionar los argumentos requeridos (if any)."
-
-	L.BS_ACTIONBAR_HELP = "Cambie la barra de acción. 'increment' lo moverá arriba de una pagina, 'decrement' hará lo contrario.  Si usted proporciona un número, la barra de acción será girada a esa página. Usted puede especificar 1,3 para alternar entre las páginas 1 y 3."
-
-	L.BS_ACTIONBAR_ARG1_LABEL = "Acción:"
-
-	L.BS_ACTION_HELP = "Simular un click sobre un botón de acción. Especificar el número del botón de acción."
-	L.BS_ACTION_ARG1_LABEL = "Número de Botón:"
-	L.BS_ACTION_ARG2_LABEL = "(Opcional) Unidad:"
-
-	L.BS_PET_HELP = "Simular un click sobre el botón de acción de mascotas.  Especificar el número de botón."
-	L.BS_PET_ARG1_LABEL = "Número de Boton Mascota:"
-	L.BS_PET_ARG2_LABEL = "(Opcional) Unidad:"
-
-	L.BS_SPELL_HELP = "Lanzar un hechizo desde el libro de hechizos.  Toma un nombre de hechizo y opcionalmente una bolsa (bag) y ranura (slot) o un nombre de ítem para usar como objetivo del hechizo (i.e. Alimentar Mascota)"
-	L.BS_SPELL_ARG1_LABEL = "Nombre del Hechizo:"
-	L.BS_SPELL_ARG2_LABEL = "*Número de Rango/Bolsa:"
-	L.BS_SPELL_ARG3_LABEL = "*Número de Ranura (Slot):"
-	L.BS_SPELL_ARG4_LABEL = "*Nombre del Item:"
-	L.BS_SPELL_ARG5_LABEL = "(Opcional) Unidad:"
-
-	L.BS_ITEM_HELP = "Use un item.  Puede tomar una bolsa y ranura o un nombre de item."
-	L.BS_ITEM_ARG1_LABEL = "Número de Bolsa:"
-	L.BS_ITEM_ARG2_LABEL = "Número de Ranura (Slot):"
-	L.BS_ITEM_ARG3_LABEL = "Nombre del Item:"
-	L.BS_ITEM_ARG4_LABEL = "(Opcional) Unidad:"
-
-	L.BS_MACRO_HELP = "Use una macro por defecto en un indice en particular"
-	L.BS_MACRO_ARG1_LABEL = "Indice Macro:"
-	L.BS_MACRO_ARG2_LABEL = "Texto Macro:"
-
-	L.BS_STOP_HELP = "Parar de lanzar hechizos en curso"
-
-	L.BS_TARGET_HELP = "Apuntar unidad"
-	L.BS_TARGET_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_FOCUS_HELP = "Fijar su \"foco\" en unidad"
-	L.BS_FOCUS_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_ASSIST_HELP = "Asistir la unidad"
-	L.BS_ASSIST_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_CLICK_HELP = "Simular click sobre un botón"
-	L.BS_CLICK_ARG1_LABEL = "Nombre del Botón:"
-
-	L.BS_MENU_HELP = "Muestra menú emergente de la unidad"
-end
-
diff --git a/Localization.esMX.lua b/Localization.esMX.lua
deleted file mode 100644
index c14f03c..0000000
--- a/Localization.esMX.lua
+++ /dev/null
@@ -1,110 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for esMX (Spanish)
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
--- This is the default locale.
-if GetLocale() == "esMX" then
-	L.RANK                    = "Rango"
-	L.RANK_PATTERN            = "Rango (%d+)"
-	L.CAST_FORMAT             = "%s(Rango %s)"
-	L.RACIAL_PASSIVE          = "Pasivo racial"
-	L.PASSIVE                 = SPELL_PASSIVE
-	L.CLICKSET_DEFAULT        = "Por Defecto"
-	L.CLICKSET_HARMFUL        = "Aciones de Daño"
-	L.CLICKSET_HELPFUL        = "Acciones de Ayuda"
-	L.CLICKSET_OOC            = "Fuera de Combate"
-	L.CLICKSET_BEARFORM       = "Forma de Oso"
-	L.CLICKSET_CATFORM        = "Forma Felina"
-    L.CLICKSET_AQUATICFORM    = "Forma Acuática"
-	L.CLICKSET_TRAVELFORM     = "Forma de Viaje"
-	L.CLICKSET_MOONKINFORM    = "Forma de Moonkin"
-	L.CLICKSET_TREEOFLIFE     = "Forma de Arbol de Vida"
-	L.CLICKSET_SHADOWFORM     = "Forma de las Sombras"
-	L.CLICKSET_STEALTHED      = "En Sigilo"
-	L.CLICKSET_BATTLESTANCE   = "Actitud de Batalla"
-	L.CLICKSET_DEFENSIVESTANCE = "Actitud Defensiva"
-	L.CLICKSET_BERSERKERSTANCE = "Actitud Rabiosa"
-
-	L.BEAR_FORM = "Forma de Oso"
-	L.DIRE_BEAR_FORM = "Forma de Oso Temible"
-	L.CAT_FORM = "Forma Felina"
-	L.AQUATIC_FORM = "Forma Acuática"
-	L.TRAVEL_FORM = "Forma de Viaje"
-	L.TREEOFLIFE = "Arbol de Vida"
-	L.MOONKIN_FORM = "Forma de Moonkin"
-	L.STEALTH = "Sigilo"
-	L.SHADOWFORM = "Forma de las Sombras"
-	L.BATTLESTANCE = "Actitud de Batalla"
-	L.DEFENSIVESTANCE = "Actitud Defensiva"
-	L.BERSERKERSTANCE = "Actitud Rabiosa"
-
-	L.BINDING_NOT_DEFINED     = "Binding no definido"
-	L.CANNOT_CHANGE_COMBAT    = "No puede hacer cambios en combate.  Los cambios se realizarán cuando salga de combate."
-	L.APPLY_QUEUE             = "Fuera de combate.  Aplicando todos los cambios pendientes."
-	L.PROFILE_CHANGED         = "El perfil ha cambiado a '%s'."
-	L.PROFILE_DELETED         = "El perfil '%s' ha sido borrado."
-	L.PROFILE_RESET         = "Su perfil '%s' ha sido resetiado."
-	L.ACTION_ACTIONBAR = "Cambiar Barra de Acción"
-	L.ACTION_ACTION = "Botón de Acción"
-	L.ACTION_PET = "Botón de Acción Mascota"
-	L.ACTION_SPELL = "Lanzar Hechizo"
-	L.ACTION_ITEM = "Usar Item"
-	L.ACTION_MACRO = "Ejecutar macro por defecto"
-	L.ACTION_STOP = "Parar Lanzamiento"
-	L.ACTION_TARGET = "Apuntar Unidad"
-	L.ACTION_FOCUS = "Fijar Foco"
-	L.ACTION_ASSIST = "Asistir Unidad"
-	L.ACTION_CLICK = "Click Botón"
-	L.ACTION_MENU = "Mostrar Menú"
-
-	L.HELP_TEXT               = "Bienvenido a Clique.  Para su operación básica, usted puede navegar por el Libro de Hechizos (Spellbook) y determinar que hechizo le gustaría asociar a un click específico.  Luego haga click sobre el hechizo, con la combinación de teclas que a usted le guste (click o click + teclado).  Por ejemplo, un sacerdote puede buscar en su libro de hechizos la \"Sanación Relámpago\" (Flash Heal) y realizar sobre este un Shift+LeftClick, lo cual asociará dicho hechizo a las combinación de teclas Shift+LeftClick."
-	L.CUSTOM_HELP             = "Esta es la pantalla de edición por defecto de Clique.  Desde aquí usted puede configurar cualquiera de las combinaciones que el UI tiene disponible para nosotros en respuesta a los clicks.  Seleccione una acción base de la columna izquierda.  Usted puede hacer clic en el botón de abajo para asociar las convinaciones de clicks + teclas que desee, y entonces proporcionar los argumentos requeridos (if any)."
-
-	L.BS_ACTIONBAR_HELP = "Cambie la barra de acción. 'increment' lo moverá arriba de una pagina, 'decrement' hará lo contrario.  Si usted proporciona un número, la barra de acción será girada a esa página. Usted puede especificar 1,3 para alternar entre las páginas 1 y 3."
-
-	L.BS_ACTIONBAR_ARG1_LABEL = "Acción:"
-
-	L.BS_ACTION_HELP = "Simular un click sobre un botón de acción. Especificar el número del botón de acción."
-	L.BS_ACTION_ARG1_LABEL = "Número de Botón:"
-	L.BS_ACTION_ARG2_LABEL = "(Opcional) Unidad:"
-
-	L.BS_PET_HELP = "Simular un click sobre el botón de acción de mascotas.  Especificar el número de botón."
-	L.BS_PET_ARG1_LABEL = "Número de Boton Mascota:"
-	L.BS_PET_ARG2_LABEL = "(Opcional) Unidad:"
-
-	L.BS_SPELL_HELP = "Lanzar un hechizo desde el libro de hechizos.  Toma un nombre de hechizo y opcionalmente una bolsa (bag) y ranura (slot) o un nombre de ítem para usar como objetivo del hechizo (i.e. Alimentar Mascota)"
-	L.BS_SPELL_ARG1_LABEL = "Nombre del Hechizo:"
-	L.BS_SPELL_ARG2_LABEL = "*Número de Rango/Bolsa:"
-	L.BS_SPELL_ARG3_LABEL = "*Número de Ranura (Slot):"
-	L.BS_SPELL_ARG4_LABEL = "*Nombre del Item:"
-	L.BS_SPELL_ARG5_LABEL = "(Opcional) Unidad:"
-
-	L.BS_ITEM_HELP = "Use un item.  Puede tomar una bolsa y ranura o un nombre de item."
-	L.BS_ITEM_ARG1_LABEL = "Número de Bolsa:"
-	L.BS_ITEM_ARG2_LABEL = "Número de Ranura (Slot):"
-	L.BS_ITEM_ARG3_LABEL = "Nombre del Item:"
-	L.BS_ITEM_ARG4_LABEL = "(Opcional) Unidad:"
-
-	L.BS_MACRO_HELP = "Use una macro por defecto en un indice en particular"
-	L.BS_MACRO_ARG1_LABEL = "Indice Macro:"
-	L.BS_MACRO_ARG2_LABEL = "Texto Macro:"
-
-	L.BS_STOP_HELP = "Parar de lanzar hechizos en curso"
-
-	L.BS_TARGET_HELP = "Apuntar unidad"
-	L.BS_TARGET_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_FOCUS_HELP = "Fijar su \"foco\" en unidad"
-	L.BS_FOCUS_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_ASSIST_HELP = "Asistir la unidad"
-	L.BS_ASSIST_ARG1_LABEL = "(Opcional) Unidad:"
-
-	L.BS_CLICK_HELP = "Simular click sobre un botón"
-	L.BS_CLICK_ARG1_LABEL = "Nombre del Botón:"
-
-	L.BS_MENU_HELP = "Muestra menú emergente de la unidad"
-end
-
diff --git a/Localization.frFR.lua b/Localization.frFR.lua
deleted file mode 100644
index 9979c77..0000000
--- a/Localization.frFR.lua
+++ /dev/null
@@ -1,23 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for frFR.  Any commented lines need to be updated
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
-if GetLocale() == "frFR" then
-	L.RANK                    = "Rang"
-	L.RANK_PATTERN            = "Rang (%d+)"
-	L.CAST_FORMAT             = "%s(Rang %d)"
-
---	L.RACIAL_PASSIVE          = "Racial Passive"
-
---	L.CLICKSET_DEFAULT        = "Default"
---	L.CLICKSET_HARMFUL        = "Harmful actions"
---	L.CLICKSET_HELPFUL        = "Helpful actions"
---	L.CLICKSET_OOC            = "Out of Combat"
-
---	L.BINDING_NOT_DEFINED     = "Binding not defined"
-
---	L.HELP_TEXT               = "Welcome to Clique.  For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click.  Then click on that spell with whatever click-binding you would like.  For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick."
---	L.CUSTOM_HELP             = "This is the Clique custom edit screen.  From here you can configure any of the combinations that the UI makes available to us in response to clicks.  Select a base action from the left column.  You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)."
-end
diff --git a/Localization.koKR.lua b/Localization.koKR.lua
deleted file mode 100644
index e8b63ec..0000000
--- a/Localization.koKR.lua
+++ /dev/null
@@ -1,23 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for koKR.  Any commented lines need to be updated
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
-if GetLocale() == "koKR" then
-	L.RANK                    = "레벨"
-	L.RANK_PATTERN            = "(%d+) 레벨"
-	L.CAST_FORMAT             = "%s(%d 레벨)"
-
---	L.RACIAL_PASSIVE          = "Racial Passive"
-
---	L.CLICKSET_DEFAULT        = "Default"
---	L.CLICKSET_HARMFUL        = "Harmful actions"
---	L.CLICKSET_HELPFUL        = "Helpful actions"
---	L.CLICKSET_OOC            = "Out of Combat"
-
---	L.BINDING_NOT_DEFINED     = "Binding not defined"
-
---	L.HELP_TEXT               = "Welcome to Clique.  For basic operation, you can navigate the spellbook and decide what spell you'd like to bind to a specific click.  Then click on that spell with whatever click-binding you would like.  For example, navigate to \"Flash Heal\" and shift-LeftClick on it to bind that spell to Shift-LeftClick."
---	L.CUSTOM_HELP             = "This is the Clique custom edit screen.  From here you can configure any of the combinations that the UI makes available to us in response to clicks.  Select a base action from the left column.  You can then click on the button below to set the binding you'd like, and then supply the arguments required (if any)."
-end
diff --git a/Localization.ruRU.lua b/Localization.ruRU.lua
deleted file mode 100644
index 43db338..0000000
--- a/Localization.ruRU.lua
+++ /dev/null
@@ -1,112 +0,0 @@
---[[---------------------------------------------------------------------------------
-    Localisation for Russian
-    Перевод на русский от Сини
-----------------------------------------------------------------------------------]]
-
-local L = Clique.Locals
-
--- This is the default locale.
-if GetLocale() == "ruRU" then
-	L.RANK                    = "Уровень"
-	L.RANK_PATTERN            = "Уровень (%d+)"
-	L.CAST_FORMAT             = "%s(Уровень %s)"
-
-	L.RACIAL_PASSIVE          = "Пассивный расовый навык"
-	L.PASSIVE                 = SPELL_PASSIVE
-
-	L.CLICKSET_DEFAULT        = "Стандартный"
-	L.CLICKSET_HARMFUL        = "Вредящие действия"
-	L.CLICKSET_HELPFUL        = "Помогающие действия"
-	L.CLICKSET_OOC            = "Вне боя"
-	L.CLICKSET_BEARFORM       = "Облик медведя"
-	L.CLICKSET_CATFORM        = "Облик кошки"
-	L.CLICKSET_AQUATICFORM    = "Водный облик"
-	L.CLICKSET_TRAVELFORM     = "Походный облик"
-	L.CLICKSET_MOONKINFORM    = "Облик лунного совуха"
-	L.CLICKSET_TREEOFLIFE     = "Древо Жизни"
-	L.CLICKSET_SHADOWFORM     = "Облик Тьмы"
-	L.CLICKSET_STEALTHED      = "Незаметность"
-	L.CLICKSET_BATTLESTANCE   = "Боевая стойка"
-	L.CLICKSET_DEFENSIVESTANCE = "Оборонительная стойка"
-	L.CLICKSET_BERSERKERSTANCE = "Стойка берсерка"
-
-	L.BEAR_FORM = "Облик медведя"
-	L.DIRE_BEAR_FORM = "Облик лютого медведя"
-	L.CAT_FORM = "Облик кошки"
-	L.AQUATIC_FORM = "Водный облик"
-	L.TRAVEL_FORM = "Походный облик"
-	L.TREEOFLIFE = "Древо Жизни"
-	L.MOONKIN_FORM = "Облик лунного совуха"
-	L.STEALTH = "Незаметность"
-	L.SHADOWFORM = "Облик Тьмы"
-	L.BATTLESTANCE = "Боевая стойка"
-	L.DEFENSIVESTANCE = "Оборонительная стойка"
-	L.BERSERKERSTANCE = "Стойка берсерка"
-
-	L.BINDING_NOT_DEFINED     = "Действие не определено"
-	L.CANNOT_CHANGE_COMBAT    = "Во время боя применить изменения невозможно.  Они будут отложены, пока Вы не выйдете из боя."
-	L.APPLY_QUEUE             = "Бой окончен.  Применяю все запланированные изменения."
-	L.PROFILE_CHANGED         = "Профиль сменен на '%s'."
-	L.PROFILE_DELETED         = "Профить '%s' был удален."
-	L.PROFILE_RESET         = "Ваш профиль '%s' был сброшен."
-
-	L.ACTION_ACTIONBAR = "Сменить панель команд"
-	L.ACTION_ACTION = "Кнопка действия"
-	L.ACTION_PET = "Кнопка действия питомца"
-	L.ACTION_SPELL = "Колдовать заклинание"
-	L.ACTION_ITEM = "Использовать предмет"
-	L.ACTION_MACRO = "Выполнить макрос"
-	L.ACTION_STOP = "Прекратить колдование"
-	L.ACTION_TARGET = "Выбрать цель"
-	L.ACTION_FOCUS = "Установить фокус"
-	L.ACTION_ASSIST = "Помочь"
-	L.ACTION_CLICK = "Щелкнуть кнопкой"
-	L.ACTION_MENU = "Показать меню"
-
-	L.HELP_TEXT               = "Добро пожаловать в Clique.  Для начала простого использования, откройте книгу заклинаний и выберите заклинание, которое Вы хотели бы назначить на определенный щелчок мышью. Затем щелкните по заклинанию именно таким щелчком, какой Вы выбрали. Например, зайдите на страницу с заклинанием \"Быстрое исцеление\" и щелкните по нему, удерживая Shift, чтобы закрепить \"Shift+Щелчок\" за этим заклинанием."
-	L.CUSTOM_HELP             = "Это экран ручной настройки Clique.  Отсюда Вы можете настроить любую комбинацию, которая предоставляется интерфейсом Warcraft как цель для щелчков мышью.  Выберите базовое действие из левой колонки. Затем Вы сможете щелкнуть по кнопке внизу, чтобы установить любое нужно действие, и ввести требуемые аргументы (если необходимо)."
-
-	L.BS_ACTIONBAR_HELP = "Сменить панель команд.  'increment' сдвинет на одну панель вверх, 'decrement' - в обратную сторону.  Если Вы введете номер, то будет выбираться соответсвующая панель команд.  Вы можете ввести 1,3 чтобы переключаться между 1-ой и 3-ей панелями."
-	L.BS_ACTIONBAR_ARG1_LABEL = "Действие:"
-
-	L.BS_ACTION_HELP = "Эмулировать щелчок мышью по кнопке действия. Введите номер кнопки действия."
-	L.BS_ACTION_ARG1_LABEL = "Номер кнопки:"
-	L.BS_ACTION_ARG2_LABEL = "(необязательно) Цель:"
-
-	L.BS_PET_HELP = "Эмулировать щелчок мышью по кнопке действия питомца. Введите номер кнопки."
-	L.BS_PET_ARG1_LABEL = "Номер кнопки питомца:"
-	L.BS_PET_ARG2_LABEL = "(необязательно) Цель:"
-
-	L.BS_SPELL_HELP = "Колдовать заклинание из книги заклинаний.  В качестве аргументов принимается название заклинания, необязательно - номер сумки и позиция внутри сумки, или название предмета как цели заклинания (например \"Накормить питомца\")."
-	L.BS_SPELL_ARG1_LABEL = "Название заклинания:"
-	L.BS_SPELL_ARG2_LABEL = "*Уровень/Номер сумки:"
-	L.BS_SPELL_ARG3_LABEL = "*Номер позиции:"
-	L.BS_SPELL_ARG4_LABEL = "*Название предмета:"
-	L.BS_SPELL_ARG5_LABEL = "(необязательно) Цель:"
-
-	L.BS_ITEM_HELP = "Использовать предмет. Принимается номер сумки и позиции внутри сумки, или название предмета."
-	L.BS_ITEM_ARG1_LABEL = "Номер сумки:"
-	L.BS_ITEM_ARG2_LABEL = "Номер позиции:"
-	L.BS_ITEM_ARG3_LABEL = "Название предмета:"
-	L.BS_ITEM_ARG4_LABEL = "(необязательно) Цель:"
-
-	L.BS_MACRO_HELP = "Использовать макрос из указанного номера ячейки."
-	L.BS_MACRO_ARG1_LABEL = "Номер ячейки макроса:"
-	L.BS_MACRO_ARG2_LABEL = "Текст макроса:"
-
-	L.BS_STOP_HELP = "Прекратить колдование текущего заклинания."
-
-	L.BS_TARGET_HELP = "Выбрать цель."
-	L.BS_TARGET_ARG1_LABEL = "(необязательно) Цель:"
-
-	L.BS_FOCUS_HELP = "Установаить \"фокус\" на цели."
-	L.BS_FOCUS_ARG1_LABEL = "(необязательно) Цель:"
-
-	L.BS_ASSIST_HELP = "Помочь цели."
-	L.BS_ASSIST_ARG1_LABEL = "(необязательно) Цель:"
-
-	L.BS_CLICK_HELP = "Эмулировать щелчок по кнопке."
-	L.BS_CLICK_ARG1_LABEL = "Название кнопки:"
-
-	L.BS_MENU_HELP = "Показать всплывающее меню цели."
-end
diff --git a/Localization.zhCN.lua b/Localization.zhCN.lua
deleted file mode 100644
index 5ebbf0e..0000000
--- a/Localization.zhCN.lua
+++ /dev/null
@@ -1,111 +0,0 @@
--------------------------------------------------------------------------------
--- Localization                                                              --
--- Simple Chinese Translated by 昏睡墨鱼&Costa<CWDG>                         --
--------------------------------------------------------------------------------
-
-local L = Clique.Locals
-
-if GetLocale() == "zhCN" then
-	L.RANK                    = "等级"
-	L.RANK_PATTERN            = "等级 (%d+)"
-	L.CAST_FORMAT             = "%s(等级 %s)"
-
-	L.RACIAL_PASSIVE          = "种族被动技能"
-	L.PASSIVE                 = SPELL_PASSIVE
-
-	L.CLICKSET_DEFAULT        = "默认"
-	L.CLICKSET_HARMFUL        = "对敌方动作"
-	L.CLICKSET_HELPFUL        = "对友方动作"
-	L.CLICKSET_OOC            = "非战斗中动作"
-	L.CLICKSET_BEARFORM       = "熊形态"
-	L.CLICKSET_CATFORM        = "猎豹形态"
-    L.CLICKSET_AQUATICFORM    = "水栖形态"
-	L.CLICKSET_TRAVELFORM     = "旅行形态"
-	L.CLICKSET_MOONKINFORM    = "枭兽形态"
-	L.CLICKSET_TREEOFLIFE     = "树形"
-	L.CLICKSET_SHADOWFORM     = "暗影形态"
-	L.CLICKSET_STEALTHED      = "潜行状态"
-	L.CLICKSET_BATTLESTANCE   = "战斗姿态"
-	L.CLICKSET_DEFENSIVESTANCE = "防御姿态"
-	L.CLICKSET_BERSERKERSTANCE = "狂暴姿态"
-
-	L.BEAR_FORM = "熊形态"
-	L.DIRE_BEAR_FORM = "巨熊形态"
-	L.CAT_FORM = "猎豹形态"
-	L.AQUATIC_FORM = "水栖形态"
-	L.TRAVEL_FORM = "旅行形态"
-	L.TREEOFLIFE = "树形"
-	L.MOONKIN_FORM = "枭兽形态"
-	L.STEALTH = "潜行状态"
-	L.SHADOWFORM = "暗影形态"
-	L.BATTLESTANCE = "战斗姿态"
-	L.DEFENSIVESTANCE = "防御姿态"
-	L.BERSERKERSTANCE = "狂暴姿态"
-
-	L.BINDING_NOT_DEFINED     = "未定义绑定"
-	L.CANNOT_CHANGE_COMBAT    = "战斗状态中无法改变. 这些改变被推迟到脱离战斗状态后."
-	L.APPLY_QUEUE             = "脱离战斗状态.  进行所有预定了的改变."
-	L.PROFILE_CHANGED         = "已经切换到设置 '%s'."
-	L.PROFILE_DELETED         = "你的设置 '%s' 已经被删除."
-	L.PROFILE_RESET         = "你的设置 '%s' 已经被重值."
-
-	L.ACTION_ACTIONBAR = "切换动作条"
-	L.ACTION_ACTION = "动作按钮"
-	L.ACTION_PET = "宠物动作按钮"
-	L.ACTION_SPELL = "释放法术"
-	L.ACTION_ITEM = "使用物品"
-	L.ACTION_MACRO = "运行自定义宏"
-	L.ACTION_STOP = "终止施法"
-	L.ACTION_TARGET = "目标单位"
-	L.ACTION_FOCUS = "指定目标单位"
-	L.ACTION_ASSIST = "协助某单位"
-	L.ACTION_CLICK = "点击按钮"
-	L.ACTION_MENU = "显示菜单"
-
-	L.HELP_TEXT               = "欢迎使用Clique.  最基本的操作,你可以浏览法术书然后给喜欢的法术绑定按键.例如,你可以找到\快速治疗\ 然后按住shift点左键,这样shift+左键就被设置为释放快速治疗了."
-	L.CUSTOM_HELP             = "这是Clique自定义窗口.在这里,你可以设置任何点击影射到界面允许的组合按键. 从左侧选择一个基本的动作,然后可以可以点击下边的按钮来映射到任何你希望的操作. "
-
-	L.BS_ACTIONBAR_HELP = "切换动作条.  'increment' 会向后翻一页, 'decrement' 则相反. 如果你提供了一个编号, 动作条就会翻到那一页.你可以定义1,3来在1和3页之间翻动."
-	L.BS_ACTIONBAR_ARG1_LABEL = "动作条:"
-
-	L.BS_ACTION_HELP = "将一种点击影射到某动作条的按钮上. 请指定动作条的编号."
-	L.BS_ACTION_ARG1_LABEL = "按钮编号:"
-	L.BS_ACTION_ARG2_LABEL = "(可选) 单位:"
-
-	L.BS_PET_HELP = "将一种点击影射到宠物动作条的按钮上.  请指定按钮编号."
-	L.BS_PET_ARG1_LABEL = "宠物按钮编号:"
-	L.BS_PET_ARG2_LABEL = "(可选) 单位:"
-
-	L.BS_SPELL_HELP = "从法术书上选择施法. 设定法术名称, 以及包裹与物品槽编号或者物品名称(可选), 来对目标进行施法 (例如喂养宠物)"
-	L.BS_SPELL_ARG1_LABEL = "法术名称:"
-	L.BS_SPELL_ARG2_LABEL = "*包裹编号:"
-	L.BS_SPELL_ARG3_LABEL = "*物品槽编号:"
-	L.BS_SPELL_ARG4_LABEL = "*物品名称:"
-	L.BS_SPELL_ARG5_LABEL = "(可选) 单位:"
-
-	L.BS_ITEM_HELP = "使用一个物品. 可以通过包裹与物品槽的编号,或者物品名称来指定."
-	L.BS_ITEM_ARG1_LABEL = "包裹编号:"
-	L.BS_ITEM_ARG2_LABEL = "物品槽编号:"
-	L.BS_ITEM_ARG3_LABEL = "物品名称:"
-	L.BS_ITEM_ARG4_LABEL = "(可选) 单位:"
-
-	L.BS_MACRO_HELP = "使用给定索引的自定义宏"
-	L.BS_MACRO_ARG1_LABEL = "宏索引:"
-	L.BS_MACRO_ARG2_LABEL = "宏内容:"
-
-	L.BS_STOP_HELP = "中断当前施法"
-
-	L.BS_TARGET_HELP = "将单位设定为目标"
-	L.BS_TARGET_ARG1_LABEL = "(可选) 单位:"
-
-	L.BS_FOCUS_HELP = "设定你的 \"目标\" 单位"
-	L.BS_FOCUS_ARG1_LABEL = "(可选) 单位:"
-
-	L.BS_ASSIST_HELP = "协助某单位"
-	L.BS_ASSIST_ARG1_LABEL = "(可选) 单位:"
-
-	L.BS_CLICK_HELP = "使用按钮模拟点击"
-	L.BS_CLICK_ARG1_LABEL = "按钮名称:"
-
-	L.BS_MENU_HELP = "显示单位的弹出菜单"
-end
\ No newline at end of file
diff --git a/Localization.zhTW.lua b/Localization.zhTW.lua
deleted file mode 100644
index 6fdb35c..0000000
--- a/Localization.zhTW.lua
+++ /dev/null
@@ -1,111 +0,0 @@
--------------------------------------------------------------------------------
--- Localization                                                              --
--- Simple Chinese Translated by 昏睡墨魚&Costa<CWDG>                         --
--------------------------------------------------------------------------------
-
-local L = Clique.Locals
-
-if GetLocale() == "zhTW" then
-	L.RANK                    = "等級"
-	L.RANK_PATTERN            = "等級 (%d+)"
-	L.CAST_FORMAT             = "%s(等級 %s)"
-
-	L.RACIAL_PASSIVE          = "種族被動技能"
-	L.PASSIVE                 = SPELL_PASSIVE
-
-	L.CLICKSET_DEFAULT        = "默認"
-	L.CLICKSET_HARMFUL        = "對敵方動作"
-	L.CLICKSET_HELPFUL        = "對友方動作"
-	L.CLICKSET_OOC            = "非戰鬥中動作"
-	L.CLICKSET_BEARFORM       = "熊形態"
-	L.CLICKSET_CATFORM        = "獵豹形態"
-    L.CLICKSET_AQUATICFORM    = "水棲形態"
-	L.CLICKSET_TRAVELFORM     = "旅行形態"
-	L.CLICKSET_MOONKINFORM    = "梟獸形態"
-	L.CLICKSET_TREEOFLIFE     = "樹形"
-	L.CLICKSET_SHADOWFORM     = "暗影形態"
-	L.CLICKSET_STEALTHED      = "潛行狀態"
-	L.CLICKSET_BATTLESTANCE   = "戰鬥姿態"
-	L.CLICKSET_DEFENSIVESTANCE = "防禦姿態"
-	L.CLICKSET_BERSERKERSTANCE = "狂暴姿態"
-
-	L.BEAR_FORM = "熊形態"
-	L.DIRE_BEAR_FORM = "巨熊形態"
-	L.CAT_FORM = "獵豹形態"
-	L.AQUATIC_FORM = "水棲形態"
-	L.TRAVEL_FORM = "旅行形態"
-	L.TREEOFLIFE = "樹形"
-	L.MOONKIN_FORM = "梟獸形態"
-	L.STEALTH = "潛行狀態"
-	L.SHADOWFORM = "暗影形態"
-	L.BATTLESTANCE = "戰鬥姿態"
-	L.DEFENSIVESTANCE = "防禦姿態"
-	L.BERSERKERSTANCE = "狂暴姿態"
-
-	L.BINDING_NOT_DEFINED     = "未定義綁定"
-	L.CANNOT_CHANGE_COMBAT    = "戰鬥狀態中無法改變. 這些改變被推遲到脫離戰鬥狀態後."
-	L.APPLY_QUEUE             = "脫離戰鬥狀態.  進行所有預定了的改變."
-	L.PROFILE_CHANGED         = "已經切換到設置 '%s'."
-	L.PROFILE_DELETED         = "你的設置 '%s' 已經被刪除."
-	L.PROFILE_RESET         = "你的設置 '%s' 已經被重值."
-
-	L.ACTION_ACTIONBAR = "切換動作條"
-	L.ACTION_ACTION = "動作按鈕"
-	L.ACTION_PET = "寵物動作按鈕"
-	L.ACTION_SPELL = "釋放法術"
-	L.ACTION_ITEM = "使用物品"
-	L.ACTION_MACRO = "運行自定義宏"
-	L.ACTION_STOP = "終止施法"
-	L.ACTION_TARGET = "目標單位"
-	L.ACTION_FOCUS = "指定目標單位"
-	L.ACTION_ASSIST = "協助某單位"
-	L.ACTION_CLICK = "點擊按鈕"
-	L.ACTION_MENU = "顯示功能表"
-
-	L.HELP_TEXT               = "歡迎使用Clique.  最基本的操作,你可以流覽法術書然後給喜歡的法術綁定按鍵.例如,你可以找到\快速治療\ 然後按住shift點左鍵,這樣shift+左鍵就被設置為釋放快速治療了."
-	L.CUSTOM_HELP             = "這是Clique自定義視窗.在這裏,你可以設置任何點擊影射到介面允許的組合按鍵. 從左側選擇一個基本的動作,然後可以可以點擊下邊的按鈕來映射到任何你希望的操作. "
-
-	L.BS_ACTIONBAR_HELP = "切換動作條.  'increment' 會向後翻一頁, 'decrement' 則相反. 如果你提供了一個編號, 動作條就會翻到那一頁.你可以定義1,3來在1和3頁之間翻動."
-	L.BS_ACTIONBAR_ARG1_LABEL = "動作條:"
-
-	L.BS_ACTION_HELP = "將一種點擊影射到某動作條的按鈕上. 請指定動作條的編號."
-	L.BS_ACTION_ARG1_LABEL = "按鈕編號:"
-	L.BS_ACTION_ARG2_LABEL = "(可選) 單位:"
-
-	L.BS_PET_HELP = "將一種點擊影射到寵物動作條的按鈕上.  請指定按鈕編號."
-	L.BS_PET_ARG1_LABEL = "寵物按鈕編號:"
-	L.BS_PET_ARG2_LABEL = "(可選) 單位:"
-
-	L.BS_SPELL_HELP = "從法術書上選擇施法. 設定法術名稱, 以及包裹與物品槽編號或者物品名稱(可選), 來對目標進行施法 (例如餵養寵物)"
-	L.BS_SPELL_ARG1_LABEL = "法術名稱:"
-	L.BS_SPELL_ARG2_LABEL = "*包裹編號:"
-	L.BS_SPELL_ARG3_LABEL = "*物品槽編號:"
-	L.BS_SPELL_ARG4_LABEL = "*物品名稱:"
-	L.BS_SPELL_ARG5_LABEL = "(可選) 單位:"
-
-	L.BS_ITEM_HELP = "使用一個物品. 可以通過包裹與物品槽的編號,或者物品名稱來指定."
-	L.BS_ITEM_ARG1_LABEL = "包裹編號:"
-	L.BS_ITEM_ARG2_LABEL = "物品槽編號:"
-	L.BS_ITEM_ARG3_LABEL = "物品名稱:"
-	L.BS_ITEM_ARG4_LABEL = "(可選) 單位:"
-
-	L.BS_MACRO_HELP = "使用給定索引的自定義巨集"
-	L.BS_MACRO_ARG1_LABEL = "巨集索引:"
-	L.BS_MACRO_ARG2_LABEL = "巨集內容:"
-
-	L.BS_STOP_HELP = "中斷當前施法"
-
-	L.BS_TARGET_HELP = "將單位設定為目標"
-	L.BS_TARGET_ARG1_LABEL = "(可選) 單位:"
-
-	L.BS_FOCUS_HELP = "設定你的 \"目標\" 單位"
-	L.BS_FOCUS_ARG1_LABEL = "(可選) 單位:"
-
-	L.BS_ASSIST_HELP = "協助某單位"
-	L.BS_ASSIST_ARG1_LABEL = "(可選) 單位:"
-
-	L.BS_CLICK_HELP = "使用按鈕類比點擊"
-	L.BS_CLICK_ARG1_LABEL = "按鈕名稱:"
-
-	L.BS_MENU_HELP = "顯示單位的彈出功能表"
-end
diff --git a/images/CliqueIcon.tga b/images/CliqueIcon.tga
deleted file mode 100644
index 0219f56..0000000
Binary files a/images/CliqueIcon.tga and /dev/null differ
diff --git a/images/RadioChecked.tga b/images/RadioChecked.tga
deleted file mode 100644
index 3b5391a..0000000
Binary files a/images/RadioChecked.tga and /dev/null differ
diff --git a/images/RadioEmpty.tga b/images/RadioEmpty.tga
deleted file mode 100644
index 6509c74..0000000
Binary files a/images/RadioEmpty.tga and /dev/null differ
diff --git a/images/backdrop.tga b/images/backdrop.tga
deleted file mode 100644
index f061cda..0000000
Binary files a/images/backdrop.tga and /dev/null differ
diff --git a/images/borders.tga b/images/borders.tga
deleted file mode 100644
index 42f9e27..0000000
Binary files a/images/borders.tga and /dev/null differ
diff --git a/images/footCorner.tga b/images/footCorner.tga
deleted file mode 100644
index 3780e58..0000000
Binary files a/images/footCorner.tga and /dev/null differ
diff --git a/images/footer.tga b/images/footer.tga
deleted file mode 100644
index 0983828..0000000
Binary files a/images/footer.tga and /dev/null differ
diff --git a/images/headCorner.tga b/images/headCorner.tga
deleted file mode 100644
index 83b28fc..0000000
Binary files a/images/headCorner.tga and /dev/null differ
diff --git a/images/header.tga b/images/header.tga
deleted file mode 100644
index 7266823..0000000
Binary files a/images/header.tga and /dev/null differ
diff --git a/images/myborder.tga b/images/myborder.tga
deleted file mode 100644
index 700c1ba..0000000
Binary files a/images/myborder.tga and /dev/null differ
diff --git a/readme.txt b/readme.txt
new file mode 100755
index 0000000..96edde7
--- /dev/null
+++ b/readme.txt
@@ -0,0 +1,78 @@
+Clique2 Design Information
+
+SavedVariable structure:
+
+self.profile.binds = {
+  ["cliqueuid:1"] = {
+    name = "Healing Touch",
+    binding = {
+      alt = true,
+      button = "1",
+    },
+    action = {
+      type = "spell",
+      spell = "Healing Touch",
+    },
+    sets = {
+      helpful = true,
+    },
+  },
+  ["cliqueuid:2"] = {
+    name = "Show menu",
+    binding = {
+      button = "2",
+    }
+    action = {
+      type = "menu",
+    },
+    sets = {
+      ooc = true,
+    },
+  },
+}
+
+Bind-set semantics (highest priority to lowest)
+
+  * custom frame set (this set does NOT inherit)
+  * out of combat set (inherits everything below, overriding)
+  * harmful and helpful
+  * default
+
+Database defaults:
+
+defaults = {
+  profile = {
+    uidCounter = 1,
+    binds = {
+      -- Show menu (default)
+      -- Target unit (default)
+    }
+  }
+}
+
+
+if not header then
+   header = CreateFrame("Button", "header", UIParent, "SecureHandlerBaseTemplate")
+end
+
+
+enter = [[
+print("Setting bindings")
+self:SetBindingClick(true, "F", self, "cliquebutton1")
+]]
+
+leave = [[
+print("Clearing bindings")
+self:ClearBinding("F")
+]]
+
+header:SetFrameRef("player", PlayerFrame)
+header:UnwrapScript(PlayerFrame, "OnEnter")
+header:UnwrapScript(PlayerFrame, "OnLeave")
+header:WrapScript(PlayerFrame, "OnEnter", enter)
+header:WrapScript(PlayerFrame, "OnLeave", leave)
+
+PlayerFrame:SetAttribute("type-cliquebutton1", "spell")
+PlayerFrame:SetAttribute("spell-cliquebutton1", "Mark of the Wild")
+PlayerFrame:SetAttribute("unit-cliquebutton1", "player")
+