changeset 109:410d036c43b2

- reorganize modularity file structure (part 1)
author Flick <flickerstreak@gmail.com>
date Thu, 08 Jan 2009 00:57:27 +0000
parents b2fb8f7dc780
children 711ed6b639ac
files Bar.lua Bindings.lua LDB.lua Overlay.lua ReAction.lua ReAction.xml State.lua modules/Action.lua modules/Bar.lua modules/Bindings.lua modules/Config.lua modules/HideBlizzard.lua modules/LBF.lua modules/LDB.lua modules/ModuleTemplate.lua modules/Overlay.lua modules/PetAction.lua modules/ReAction_Action/ReAction_Action.lua modules/ReAction_Action/ReAction_Action.toc modules/ReAction_Action/ReAction_Action.xml modules/ReAction_ButtonFacade/ReAction_ButtonFacade.lua modules/ReAction_ButtonFacade/ReAction_ButtonFacade.toc modules/ReAction_ButtonFacade/ReAction_ButtonFacade.xml modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua modules/ReAction_HideBlizzard/ReAction_HideBlizzard.toc modules/ReAction_HideBlizzard/ReAction_HideBlizzard.xml modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua modules/ReAction_ModuleTemplate/ReAction_ModuleName.toc modules/ReAction_ModuleTemplate/ReAction_ModuleName.xml modules/ReAction_PetAction/ReAction_PetAction.lua modules/ReAction_PetAction/ReAction_PetAction.toc modules/ReAction_PetAction/ReAction_PetAction.xml modules/State.lua modules/modules.xml
diffstat 34 files changed, 5049 insertions(+), 4556 deletions(-) [+]
line wrap: on
line diff
--- a/Bar.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,245 +0,0 @@
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local CreateFrame = CreateFrame
-local floor = math.floor
-local fmod = math.fmod
-local format = string.format
-
-ReAction:UpdateRevision("$Revision$")
-
-local Bar   = { }
-local proto = { __index = Bar }
-local weak  = { __mode = "k" }
-
-ReAction.Bar = Bar -- export to ReAction
-
-function Bar:New( name, config )
-  if type(config) ~= "table" then
-    error("ReAction.Bar: config table required")
-  end
-
-  -- create new self
-  self = setmetatable( 
-    { 
-      config  = config,
-      name    = name,
-      buttons = setmetatable( { }, weak ),
-      width   = config.width or 480,
-      height  = config.height or 40
-    }, 
-    proto )
-  
-  -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
-  -- not mouse-clickable by the user.
-  local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
-  local f = CreateFrame("Button", name and format("ReAction-%s",name), parent,
-                        "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
-  f:SetFrameStrata("MEDIUM")
-  f:SetWidth(self.width)
-  f:SetWidth(self.height)
-  f:SetAlpha(config.alpha or 1.0)
-  f:Show()
-  f:EnableMouse(false)
-  f:SetClampedToScreen(true)
-
-  -- Override the default frame accessor to provide strict read-only access
-  function self:GetFrame()
-    return f
-  end
-
-  self:ApplyAnchor()
-  ReAction.RegisterCallback(self, "OnConfigModeChanged")
-
-  return self
-end
-
-function Bar:Destroy()
-  local f = self:GetFrame()
-  f:UnregisterAllEvents()
-  ReAction.UnregisterAllCallbacks(self)
-  f:Hide()
-  f:SetParent(UIParent)
-  f:ClearAllPoints()
-end
-
-function Bar:OnConfigModeChanged(event, mode)
-  self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
-end
-
-function Bar:ApplyAnchor()
-  local f = self:GetFrame()
-  local c = self.config
-  local p = c.point
-
-  f:SetWidth(c.width)
-  f:SetHeight(c.height)
-  f:ClearAllPoints()
-  
-  if p then
-    local a = f:GetParent()
-    if c.anchor then
-      local bar = ReAction:GetBar(c.anchor)
-      if bar then
-        a = bar:GetFrame()
-      else
-        a = _G[c.anchor]
-      end
-    end
-    local fr = a or f:GetParent()
-    f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
-  else
-    f:SetPoint("CENTER")
-  end
-end
-
-function Bar:SetAnchor(point, frame, relativePoint, x, y)
-  local c = self.config
-  c.point = point or c.point
-  c.anchor = frame or c.anchor
-  c.relpoint = relativePoint or c.relpoint
-  c.x = x or c.x
-  c.y = y or c.y
-  self:ApplyAnchor()
-  ReAction:RefreshBar(self)
-end
-
-function Bar:GetAnchor()
-  local c = self.config
-  return (c.point or "CENTER"), 
-         (c.anchor or self:GetFrame():GetParent():GetName()), 
-         (c.relpoint or c.point or "CENTER"), 
-         (c.x or 0), 
-         (c.y or 0)
-end
-
-function Bar:GetSize()
-  local f = self:GetFrame()
-  return f:GetWidth(), f:GetHeight()
-end
-
-function Bar:SetSize(w,h)
-  local f = self:GetFrame()
-  self.config.width = w
-  self.config.height = h
-  f:SetWidth(w)
-  f:SetHeight(h)
-end
-
-function Bar:GetButtonSize()
-  local w = self.config.btnWidth or 32
-  local h = self.config.btnHeight or 32
-  -- TODO: get from modules?
-  return w,h
-end
-
-function Bar:SetButtonSize(w,h)
-  if w > 0 and h > 0 then
-    self.config.btnWidth = w
-    self.config.btnHeight = h
-  end
-  ReAction:RefreshBar(self)
-end
-
-function Bar:GetButtonGrid()
-  local cfg = self.config
-  local r = cfg.btnRows or 1
-  local c = cfg.btnColumns or 1
-  local s = cfg.spacing or 4
-  return r,c,s
-end
-
-function Bar:GetNumButtons()
-  local r,c = self:GetButtonGrid()
-  return r*c
-end
-
-function Bar:SetButtonGrid(r,c,s)
-  if r > 0 and c > 0 and s > 0 then
-    local cfg = self.config
-    cfg.btnRows = r
-    cfg.btnColumns = c
-    cfg.spacing = s
-  end
-  ReAction:RefreshBar(self)
-end
-
-function Bar:ClipNButtons( n )
-  local cfg = self.config
-  local r = cfg.btnRows or 1
-  local c = cfg.btnColumns or 1
-
-  cfg.btnRows = ceil(n/c)
-  cfg.btnColumns = min(n,c)
-end
-
-function Bar:GetName()
-  return self.name
-end
-
-function Bar:GetFrame()
-  -- this method is included for documentation purposes. It is overridden
-  -- for each object in the :New() method.
-  error("Invalid Bar object: used without initialization")
-end
-
--- only ReAction:RenameBar() should call this function. Calling from any other
--- context will desync the bar list in the ReAction class.
-function Bar:SetName(name)
-  self.name = name
-  self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua
-end
-
-function Bar:GetAlpha()
-  return self.config.alpha or 1.0
-end
-
-function Bar:SetAlpha(value)
-  self.config.alpha = value
-  self:GetFrame():SetAlpha(value or 1.0)
-  ReAction:RefreshBar(self)
-end
-
-function Bar:AddButton(idx, button)
-  local f = self:GetFrame()
-
-  -- store in a weak reverse-index array
-  self.buttons[button] = idx
-
-  -- Store a properly wrapped reference to the child frame as an attribute 
-  -- (accessible via "frameref-btn#")
-  f:SetFrameRef(format("btn%d",idx), button:GetFrame())
-end
-
-function Bar:RemoveButton(button)
-  local idx = self.buttons[button]
-  if idx then
-    self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
-    self.buttons[button] = nil
-  end
-end
-
--- iterator returns button, idx and does NOT iterate in index order
-function Bar:IterateButtons()
-  return pairs(self.buttons)
-end
-
-function Bar:PlaceButton(button, baseW, baseH)
-  local idx = self.buttons[button]
-  if idx then 
-    local r, c, s = self:GetButtonGrid()
-    local bh, bw = self:GetButtonSize()
-    local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
-    local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
-    local scale = bw/baseW
-    local b = button:GetFrame()
-
-    b:ClearAllPoints()
-    b:SetPoint("TOPLEFT",x/scale,y/scale)
-    b:SetScale(scale)
-  end
-end
-
-function Bar:SkinButton()
-  -- does nothing by default
-end
\ No newline at end of file
--- a/Bindings.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-local L = LibStub("AceLocale-3.0"):GetLocale("ReAction")
-local _G = _G
-
-_G["BINDING_HEADER_REACTION"]             = L["ReAction"]
-
-_G["BINDING_NAME_REACTION_TOGGLELOCK"]    = L["Toggle ReAction Bar Lock"]
-_G["BINDING_NAME_REACTION_TOGGLEKEYBIND"] = L["ReAction Keybinding Mode"]
--- a/LDB.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,36 +0,0 @@
-local ReAction = ReAction
-local L = ReAction.L
-local format = string.format
-
--- Export ReAction launcher to LibDataBroker-aware displays
-LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject( "ReAction", 
-  {
-    type = "launcher", 
-    icon = "Interface\\Icons\\INV_Qiraj_JewelEncased",
-
-    OnClick = function( frame, button )
-      if not InCombatLockdown() then
-        if IsAltKeyDown() then
-          ReAction:SetKeybindMode( not ReAction:GetKeybindMode() )
-        elseif IsShiftKeyDown() then
-          ReAction:SetConfigMode( not ReAction:GetConfigMode() )
-        elseif button == "RightButton" then
-          ReAction:ShowEditor()
-        else
-          ReAction:ShowConfig()
-        end
-      else
-        ReAction:UserError(L["ReAction: can't configure in combat"])
-      end
-    end,
-
-      -- this isn't included in the 'launcher' type LDB spec but it seems all launcher displays use it
-    OnTooltipShow = function( tooltip )
-      tooltip:AddLine(format("|cffffffff%s|r %s",L["Click"],L["for global configuration"]))
-      tooltip:AddLine(format("|cffffd200%s|r %s",L["Right-click"],L["for bar editor dialog"]))
-      tooltip:AddLine(format("|cff00ff00%s|r %s",L["Shift-click"],L["to unlock bars"]))
-      tooltip:AddLine(format("|cff00cccc%s|r %s",L["Alt-click"],L["for keybind mode"]))
-    end,
-
-  }
-)
--- a/Overlay.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,769 +0,0 @@
-local ReAction               = ReAction
-local L                      = ReAction.L
-local CreateFrame            = CreateFrame
-local InCombatLockdown       = InCombatLockdown
-local floor                  = math.floor
-local min                    = math.min
-local format                 = string.format
-local GameTooltip            = GameTooltip
-local Bar                    = ReAction.Bar
-local GetSize                = Bar.GetSize
-local SetSize                = Bar.SetSize
-local GetButtonSize          = Bar.GetButtonSize
-local GetButtonGrid          = Bar.GetButtonGrid
-local SetButtonSize          = Bar.SetButtonSize
-local SetButtonGrid          = Bar.SetButtonGrid
-local ApplyAnchor            = Bar.ApplyAnchor
-local GameTooltipTextRight1  = GameTooltipTextRight1
-local GameTooltipTextRight2  = GameTooltipTextRight2
-local GameTooltipTextRight3  = GameTooltipTextRight3
-
-local KB = LibStub("LibKeyBound-1.0")
-
-ReAction:UpdateRevision("$Revision$")
-
-
---
--- Wrap some of the bar manipulators to make them state-aware
---
-local function SetAnchor( bar, point, frame, relPoint, x, y )
-  local state = bar:GetState()
-  if state then
-    local anchorstate = bar:GetStateProperty(state, "anchorEnable")
-    if anchorstate then
-      bar:SetStateProperty(state, "anchorFrame", frame)
-      bar:SetStateProperty(state, "anchorPoint", point)
-      bar:SetStateProperty(state, "anchorRelPoint", relPoint)
-      bar:SetStateProperty(state, "anchorX", x or 0)
-      bar:SetStateProperty(state, "anchorY", y or 0)
-      bar:SetAnchor(bar:GetAnchor())
-      return
-    end
-  end
-  bar:SetAnchor(point, frame, relPoint, x, y)
-end
-
-local function GetStateScale( bar )
-  local state = bar:GetState()
-  if state and bar:GetStateProperty(state, "enableScale") then
-    return bar:GetStateProperty(state, "scale")
-  end
-end
-
-local function SetStateScale( bar, scale )
-  local state = bar:GetState()
-  if state and bar:GetStateProperty(state, "enableScale") then
-    bar:SetStateProperty(state, "scale", scale)
-  end
-end
-
-
---
--- Bar config overlay
---
-
-local function GetNormalTextColor()
-  return 1.0, 1.0, 1.0, 1.0
-end
-
-local function GetAnchoredTextColor()
-  return 1.0, 1.0, 1.0, 1.0
-end
-
-local function GetNormalBgColor()
-  return 0.7, 0.7, 1.0, 0.3
-end
-
-local function GetAnchoredBgColor()
-  return 0.9, 0.2, 0.7, 0.3
-end
-
-local function StoreSize(bar)
-  local f = bar:GetFrame()
-  SetSize( bar, f:GetWidth(), f:GetHeight() )
-end
-
-local function StoreExtents(bar)
-  local f = bar:GetFrame()
-  local p, fr, rp, x, y = f:GetPoint(1)
-  fr = fr and fr:GetName() or "UIParent"
-  SetAnchor( bar, p, fr, rp, x, y )
-  SetSize( bar, f:GetWidth(), f:GetHeight() )
-end
-
-local function RecomputeButtonSize(bar)
-  local w, h = GetSize(bar)
-  local bw, bh = GetButtonSize(bar)
-  local r, c, s = GetButtonGrid(bar)
-
-  local scaleW = (floor(w/c) - s) / bw
-  local scaleH = (floor(h/r) - s) / bh
-  local scale = min(scaleW, scaleH)
-
-  SetButtonSize(bar, scale * bw, scale * bh, s)
-end
-
-local function ComputeBarScale(bar)
-  local w, h = bar.controlFrame:GetWidth() - 8, bar.controlFrame:GetHeight() - 8
-  local bw, bh = GetButtonSize(bar)
-  local r, c, s = GetButtonGrid(bar)
-
-  local scaleW = w / (c*(bw+s))
-  local scaleH = h / (r*(bh+s))
-  local scale = min(scaleW, scaleH)
-
-  if scale > 2.5 then
-    scale = 2.5
-  elseif scale < 0.25 then
-    scale = 0.25
-  end
-
-  return scale
-end
-
-local function RecomputeButtonSpacing(bar)
-  local w, h = GetSize(bar)
-  local bw, bh = GetButtonSize(bar)
-  local r, c, s = GetButtonGrid(bar)
-
-  SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh))
-end
-
-local function RecomputeGrid(bar)
-  local w, h = GetSize(bar)
-  local bw, bh = GetButtonSize(bar)
-  local r, c, s = GetButtonGrid(bar)
-
-  SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s)
-end
-
-local function ClampToButtons(bar)
-  local bw, bh = GetButtonSize(bar)
-  local r, c, s = GetButtonGrid(bar)
-  SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1)
-end
-
-local function HideGameTooltip()
-  GameTooltip:Hide()
-end
-
-local anchorInside  = { inside = true }
-local anchorOutside = { outside = true }
-local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" }
-local oppositeEdges = {
-  TOP = "BOTTOM",
-  BOTTOM = "TOP",
-  LEFT = "RIGHT",
-  RIGHT = "LEFT"
-}
-local pointsOnEdge = {
-  BOTTOM = { "BOTTOM", "BOTTOMLEFT",  "BOTTOMRIGHT",  },
-  TOP    = { "TOP",    "TOPLEFT",     "TOPRIGHT",     },
-  RIGHT  = { "RIGHT",  "BOTTOMRIGHT", "TOPRIGHT",     },
-  LEFT   = { "LEFT",   "BOTTOMLEFT",  "TOPLEFT",      },
-}
-local edgeSelector = {
-  BOTTOM = 1,  -- select x of x,y
-  TOP    = 1,  -- select x of x,y
-  LEFT   = 2,  -- select y of x,y
-  RIGHT  = 2,  -- select y of x,y  
-}
-local snapPoints = {
-  [anchorOutside] = {
-    BOTTOMLEFT  = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"},
-    BOTTOM      = {"TOP"},
-    BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"},
-    RIGHT       = {"LEFT"},
-    TOPRIGHT    = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"},
-    TOP         = {"BOTTOM"},
-    TOPLEFT     = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"},
-    LEFT        = {"RIGHT"},
-    CENTER      = {"CENTER"}
-  },
-  [anchorInside] = {
-    BOTTOMLEFT  = {"BOTTOMLEFT"},
-    BOTTOM      = {"BOTTOM"},
-    BOTTOMRIGHT = {"BOTTOMRIGHT"},
-    RIGHT       = {"RIGHT"},
-    TOPRIGHT    = {"TOPRIGHT"},
-    TOP         = {"TOP"},
-    TOPLEFT     = {"TOPLEFT"},
-    LEFT        = {"LEFT"},
-    CENTER      = {"CENTER"}
-  }
-}
-local insidePointOffsetFuncs = {
-  BOTTOMLEFT  = function(x, y) return x, y end,
-  BOTTOM      = function(x, y) return 0, y end,
-  BOTTOMRIGHT = function(x, y) return -x, y end,
-  RIGHT       = function(x, y) return -x, 0 end,
-  TOPRIGHT    = function(x, y) return -x, -y end,
-  TOP         = function(x, y) return 0, -y end,
-  TOPLEFT     = function(x, y) return x, -y end,
-  LEFT        = function(x, y) return x, 0 end,
-  CENTER      = function(x, y) return x, y end,
-}
-local pointCoordFuncs = {
-  BOTTOMLEFT  = function(f) return f:GetLeft(),  f:GetBottom() end,
-  BOTTOM      = function(f) return nil,          f:GetBottom() end,
-  BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end,
-  RIGHT       = function(f) return f:GetRight(), nil end,
-  TOPRIGHT    = function(f) return f:GetRight(), f:GetTop() end,
-  TOP         = function(f) return nil,          f:GetTop() end,
-  TOPLEFT     = function(f) return f:GetLeft(),  f:GetTop() end,
-  LEFT        = function(f) return f:GetLeft(),  nil end,
-  CENTER      = function(f) return f:GetCenter() end,
-}
-local edgeBoundsFuncs = {
-  BOTTOM = function(f) return f:GetLeft(), f:GetRight() end,
-  LEFT   = function(f) return f:GetBottom(), f:GetTop() end
-}
-edgeBoundsFuncs.TOP   = edgeBoundsFuncs.BOTTOM
-edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT
-local cornerTexCoords = {
-              -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy
-  TOPLEFT     = { 1,   1,   1,   0,   0,   1,   0,   0 },
-  TOPRIGHT    = { 1,   0,   0,   0,   1,   1,   0,   1 },
-  BOTTOMLEFT  = { 0,   1,   1,   1,   0,   0,   1,   0 },
-  BOTTOMRIGHT = { 0,   0,   0,   1,   1,   0,   1,   1 },
-}
-
--- Returns absolute coordinates x,y of the named point 'p' of frame 'f'
-local function GetPointCoords( f, p )
-  local x, y = pointCoordFuncs[p](f)
-  if not(x and y) then
-    local cx, cy = f:GetCenter()
-    x = x or cx
-    y = y or cy
-  end
-  return x, y
-end
-
-
--- Returns true if frame 'f1' can be anchored to frame 'f2'
-local function CheckAnchorable( f1, f2 )
-  -- can't anchor a frame to itself or to nil
-  if f1 == f2 or f2 == nil then
-    return false
-  end
-  
-  -- can always anchor to UIParent
-  if f2 == UIParent then
-    return true
-  end
-  
-  -- also can't do circular anchoring of frames 
-  -- walk the anchor chain, which generally shouldn't be that expensive
-  -- (who nests draggables that deep anyway?)
-  for i = 1, f2:GetNumPoints() do
-    local _, f = f2:GetPoint(i)
-    if not f then f = f2:GetParent() end
-    return CheckAnchorable(f1,f)
-  end
-  
-  return true
-end
-
--- Returns true if frames f1 and f2 specified edges overlap
-local function CheckEdgeOverlap( f1, f2, e )
-  local l1, u1 = edgeBoundsFuncs[e](f1)
-  local l2, u2 = edgeBoundsFuncs[e](f2)
-  return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2
-end
-
--- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2
-local function CheckPointEdgeOverlap( f1, p1, f2, e2 )
-  local l, u = edgeBoundsFuncs[e2](f2)
-  local x, y = GetPointCoords(f1,p1)
-  x = select(edgeSelector[e2], x, y)
-  return l <= x and x <= u
-end
-
--- Returns the distance between corresponding edges. It is 
--- assumed that the passed in edges e1 and e2 are the same or opposites
-local function GetEdgeDistance( f1, f2, e1, e2 )
-  local x1, y1 = pointCoordFuncs[e1](f1)
-  local x2, y2 = pointCoordFuncs[e2](f2)
-  return math.abs((x1 or y1) - (x2 or y2))
-end
-
-local globalSnapTargets = { [UIParent] = anchorInside }
-
-local function GetClosestFrameEdge(f1,f2,a)
-  local dist, edge, opp
-  if f2:IsVisible() and CheckAnchorable(f1,f2) then
-    for _, e in pairs(edges) do
-      local o = a.inside and e or oppositeEdges[e]
-      if CheckEdgeOverlap(f1,f2,e) then
-        local d = GetEdgeDistance(f1, f2, e, o)
-        if not dist or (d < dist) then
-          dist, edge, opp = d, e, o
-        end
-      end
-    end
-  end
-  return dist, edge, opp
-end
-
-local function GetClosestVisibleEdge( f )
-  local r, o, e1, e2
-  local a = anchorOutside
-  for _, b in ReAction:IterateBars() do
-    local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a)
-    if d and (not r or d < r) then
-      r, o, e1, e2 = d, b:GetFrame(), e, opp
-    end
-  end
-  for f2, a2 in pairs(globalSnapTargets) do
-    local d, e, opp = GetClosestFrameEdge(f,f2,a2)
-    if d and (not r or d < r) then
-      r, o, e1, e2, a = d, f2, e, opp, a2
-    end
-  end
-  return o, e1, e2, a
-end
-
-local function GetClosestVisiblePoint(f1)
-  local f2, e1, e2, a = GetClosestVisibleEdge(f1)
-  if f2 then
-    local rsq, p, rp, x, y
-    -- iterate pointsOnEdge in order and use < to prefer edge centers to corners
-    for _, p1 in ipairs(pointsOnEdge[e1]) do
-      if CheckPointEdgeOverlap(f1,p1,f2,e2) then
-        for _, p2 in pairs(snapPoints[a][p1]) do
-          local x1, y1 = GetPointCoords(f1,p1)
-          local x2, y2 = GetPointCoords(f2,p2)
-          local dx = x1 - x2
-          local dy = y1 - y2
-          local rsq2 = dx*dx + dy*dy
-          if not rsq or rsq2 < rsq then
-            rsq, p, rp, x, y = rsq2, p1, p2, dx, dy
-          end
-        end
-      end
-    end
-    return f2, p, rp, x, y
-  end
-end
-
-local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff)
-  local o, p, rp, x, y = GetClosestVisiblePoint(f1)
-  local s = false
-
-  local insideOffsetFunc = p and insidePointOffsetFuncs[p]
-  local coordFunc = p and pointCoordFuncs[p]
-  if not insideOffsetFunc or not coordFunc then
-    return
-  end
-  
-  local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0)
-  local xx, yy = coordFunc(f1)
-  if xx and yy then
-    if math.abs(x) <= rx then
-      if math.abs(y) <= ry then
-        x = sx
-        y = sy
-        s = true
-      elseif CheckEdgeOverlap(f1,o,"LEFT") then
-        x = sx
-        s = true
-      end
-    elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then
-      y = sy
-      s = true
-    end
-  elseif xx then
-    if math.abs(x) <= rx then
-      x = sx
-      s = true
-      if math.abs(y) <= ry then
-        y = sy
-      end
-    end
-  elseif yy then
-    if math.abs(y) <= ry then
-      y = sy
-      s = true
-      if math.abs(x) <= rx then
-        x = sx
-      end
-    end
-  end
-
-  -- correct for some Lua oddities with doubles
-  if x == -0 then x = 0 end
-  if y == -0 then y = 0 end
-  
-  if s then
-    return o, p, rp, math.floor(x), math.floor(y)
-  end
-end
-
-local function CreateSnapIndicator()
-  local si = CreateFrame("Frame",nil,UIParent)
-  si:SetFrameStrata("HIGH")
-  si:SetHeight(16)
-  si:SetWidth(16)
-  local tex = si:CreateTexture()
-  tex:SetAllPoints()
-  tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
-  tex:SetBlendMode("ADD")
-  tex:SetDrawLayer("OVERLAY")
-  return si
-end
-
-local si1 = CreateSnapIndicator()
-local si2 = CreateSnapIndicator()
-
-local function DisplaySnapIndicator( f, rx, ry, xOff, yOff )
-  local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff)
-  if o then
-    si1:ClearAllPoints()
-    si2:ClearAllPoints()
-    si1:SetPoint("CENTER", f, p, 0, 0)
-    local xx, yy = pointCoordFuncs[rp](o)
-    x = math.abs(x) <=rx and xx and 0 or x
-    y = math.abs(y) <=ry and yy and 0 or y
-    si2:SetPoint("CENTER", o, rp, x, y)
-    si1:Show()
-    si2:Show()
-  else
-    if si1:IsVisible() then
-      si1:Hide()
-      si2:Hide()
-    end
-  end
-  return o, p
-end
-
-local function HideSnapIndicator()
-  if si1:IsVisible() then
-    si1:Hide()
-    si2:Hide()
-  end
-end
-
-local function UpdateLabelString(bar)
-  local label = bar.controlLabelString
-  if label then
-    local name = bar.labelName
-    if name and bar.labelSubtext then
-      name = format("%s (%s)", name, bar.labelSubtext)
-    end
-    label:SetText(name or "")
-  end
-end
-
-local function CreateControls(bar)
-  local f = bar:GetFrame()
-
-  f:SetMovable(true)
-  f:SetResizable(true)
-
-  -- child of UIParent so that alpha and scale doesn't propagate to it
-  local overlay = CreateFrame("Button", nil, UIParent)
-  overlay:EnableMouse(true)
-  overlay:SetFrameLevel(3) -- set it above the buttons
-  overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4)
-  overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4)
-  overlay:SetBackdrop({
-    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
-    tile = true,
-    tileSize = 16,
-    edgeSize = 16,
-    insets = { left = 0, right = 0, top = 0, bottom = 0 },
-  })
-
-  -- textures
-  local bgTex = overlay:CreateTexture(nil,"BACKGROUND")
-  bgTex:SetTexture(0.7,0.7,1.0,0.2)
-  bgTex:SetPoint("TOPLEFT",4,-4)
-  bgTex:SetPoint("BOTTOMRIGHT",-4,4)
-  local hTex = overlay:CreateTexture(nil,"HIGHLIGHT")
-  hTex:SetTexture(0.7,0.7,1.0,0.2)
-  hTex:SetPoint("TOPLEFT",4,-4)
-  hTex:SetPoint("BOTTOMRIGHT",-4,4)
-  hTex:SetBlendMode("ADD")
-  local aTex = overlay:CreateTexture(nil,"ARTWORK")
-  aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
-  aTex:SetWidth(16)
-  aTex:SetHeight(16)
-  aTex:Hide()
-
-  -- label
-  local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
-  label:SetAllPoints()
-  label:SetJustifyH("CENTER")
-  label:SetShadowColor(0,0,0,1)
-  label:SetShadowOffset(3,-3)
-  label:SetTextColor(GetNormalTextColor())
-  label:SetText(bar:GetName())
-  label:Show()
-  bar.controlLabelString = label  -- so that bar:SetLabel() can update it
-
-  local function UpdateAnchorDecoration()
-    local point, anchor, relPoint, x, y = f:GetPoint(1)
-    if point then
-      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
-      if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then
-        bgTex:SetTexture( GetAnchoredBgColor() )
-        hTex:SetTexture( GetAnchoredBgColor() )
-        label:SetTextColor( GetAnchoredTextColor() )
-        aTex:ClearAllPoints()
-        aTex:SetPoint(point)
-        aTex:Show()
-        return
-      end
-    end
-    bgTex:SetTexture( GetNormalBgColor() )
-    hTex:SetTexture( GetNormalBgColor() )
-    label:SetTextColor( GetNormalTextColor() )
-    aTex:Hide()
-  end
-
-  local function StopResize()
-    f:StopMovingOrSizing()
-    f.isMoving = false
-    f:SetScript("OnUpdate",nil)
-    StoreSize(bar)
-    ClampToButtons(bar)
-    ReAction:RefreshOptions()
-  end
-
-  local function CornerUpdate()
-    local bw, bh = GetButtonSize(bar)
-    local r, c, s = GetButtonGrid(bar)
-    local ss = GetStateScale(bar)
-    if IsShiftKeyDown() then
-      if ss then
-        f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss )
-        f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss )
-        scale = ComputeBarScale(bar)
-      else
-        f:SetMinResize( (s+12)*c+1, (s+12)*r+1 )
-        f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 )
-        RecomputeButtonSize(bar)
-      end
-    elseif not ss and IsAltKeyDown() then
-      f:SetMinResize( bw*c, bh*r )
-      f:SetMaxResize( 2*bw*c, 2*bh*r )
-      RecomputeButtonSpacing(bar)
-    else
-      f:SetMinResize( bw+s+1, bh+s+1 )
-      f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 )
-      RecomputeGrid(bar)
-    end
-    GameTooltipTextRight2:SetText(format("%d x %d",r,c))
-
-    local ss = GetStateScale(bar)
-    local state = bar:GetState()
-    if ss then
-      GameTooltipTextRight4:SetText(format("%d%%", scale*100))
-    else
-      local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
-      GameTooltipTextRight3:SetText(size)
-      GameTooltipTextRight4:SetText(tostring(s))
-    end
-  end
-
-  -- corner drag handles
-  for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do
-    local corner = CreateFrame("Frame",nil,overlay)
-    corner:EnableMouse(true)
-    corner:SetWidth(16)
-    corner:SetHeight(16)
-    corner:SetPoint(point)
-
-    local tex = corner:CreateTexture(nil,"HIGHLIGHT")
-    tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner")
-    tex:SetTexCoord(unpack(cornerTexCoords[point]))
-    tex:SetBlendMode("ADD")
-    tex:SetAlpha(0.6)
-    tex:SetAllPoints()
-    
-    corner:SetScript("OnMouseDown",
-      function(_,btn)
-        f:SetScript("OnUpdate", CornerUpdate)
-        f:StartSizing(point)
-      end
-    )
-    corner:SetScript("OnMouseUp",
-      function()
-        local ss = GetStateScale(bar)
-        if ss then
-          local state = bar:GetState()
-          SetStateScale(bar, ComputeBarScale(bar))
-        end
-        StopResize()
-      end)
-    corner:SetScript("OnEnter",
-      function()
-        local bw, bh = GetButtonSize(bar)
-        local r, c, s = bar:GetButtonGrid()
-        local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
-        local ss = GetStateScale(bar)
-        local state = bar:GetState()
-        GameTooltip:SetOwner(f, "ANCHOR_"..point)
-        if ss then
-          GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
-        else
-          GameTooltip:AddLine(bar:GetName())
-        end
-        GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c))
-        if ss then
-          GameTooltip:AddLine(L["State Scale Override"])
-          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100))
-        else
-          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size)))
-          GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"],  L["to change spacing:"]), tostring(floor(s)))
-        end
-        GameTooltip:Show()
-      end
-    )
-    corner:SetScript("OnLeave", 
-      function()
-        GameTooltip:Hide()
-        f:SetScript("OnUpdate",nil)
-      end
-    )
-  end
-
-  overlay:RegisterForDrag("LeftButton")
-  overlay:RegisterForClicks("RightButtonUp")
-  
-  overlay:SetScript("OnDragStart",
-    function()
-      f:StartMoving()
-      f.isMoving = true
-      local w,h = bar:GetButtonSize()
-      f:ClearAllPoints()
-      UpdateAnchorDecoration()
-      f:SetScript("OnUpdate", function()
-          if IsShiftKeyDown() then
-            local f, p = DisplaySnapIndicator(f,w,h)
-          else
-            HideSnapIndicator()
-          end
-        end)
-    end
-  )
-
-  local function UpdateDragTooltip()
-    GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT")
-    local ss = GetStateScale(bar)
-    local state = bar:GetState()
-    if ss then
-      GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
-    else
-      GameTooltip:AddLine(bar:GetName())
-    end
-    GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"]))
-    GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"]))
-    GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."]))
-    local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1)
-    if point then
-      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
-      if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then
-        frame = frame or UIParent
-        GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName()))
-      end
-    end
-    GameTooltip:Show()
-  end
-
-  overlay:SetScript("OnDragStop",
-    function()
-      f:StopMovingOrSizing()
-      f.isMoving = false
-      f:SetScript("OnUpdate",nil)
-
-      if IsShiftKeyDown() then
-        local w, h = bar:GetButtonSize()
-        local a, p, rp, x, y = GetClosestPointSnapped(f,w,h)
-        if a then
-          f:ClearAllPoints()
-          f:SetPoint(p,a,rp,x,y)
-        end
-        HideSnapIndicator()
-      end
-
-      StoreExtents(bar)
-      ReAction:RefreshOptions()
-      UpdateDragTooltip()
-      UpdateAnchorDecoration()
-    end
-  )
-
-  overlay:SetScript("OnEnter",
-    function()
-      UpdateDragTooltip()
-    end
-  )
-
-  overlay:SetScript("OnLeave", HideGameTooltip)
-
-  overlay:SetScript("OnClick",
-    function()
-      ReAction:ShowEditor(bar)
-    end
-  )
-
-  function overlay:LIBKEYBOUND_ENABLED(evt)
-    self:SetFrameLevel(1)
-  end
-
-  function overlay:LIBKEYBOUND_DISABLED(evt)
-    self:SetFrameLevel(3)
-  end
-
-  function overlay:RefreshControls()
-    UpdateAnchorDecoration()
-  end
-
-  overlay:SetScript("OnShow", overlay.RefreshControls)
-
-  KB.RegisterCallback(overlay,"LIBKEYBOUND_ENABLED")
-  KB.RegisterCallback(overlay,"LIBKEYBOUND_DISABLED")
-
-  if ReAction:GetKeybindMode() then
-    overlay:SetFrameLevel(1)
-  end
-
-  bar:SetLabel(bar:GetName())
-  UpdateLabelString(bar)
-  UpdateAnchorDecoration()
-
-  return overlay
-end
-
-
--- export methods to the Bar prototype
-
-function Bar:ShowControls(show)
-  local f = self.controlFrame
-  if show then
-    if not f then
-      f = CreateControls(self)
-      self.controlFrame = f
-    end
-    f:Show()
-  elseif f then
-    f:Hide()
-  end
-end
-
-function Bar:RefreshControls()
-  if self.controlFrame and self.controlFrame:IsShown() then
-    self.controlFrame:RefreshControls()
-  end
-end
-
-function Bar:SetLabel(name)
-  self.labelName = name
-  UpdateLabelString(self)
-end
-
-function Bar:SetLabelSubtext(text)
-  self.labelSubtext = text
-  UpdateLabelString(self)
-end
--- a/ReAction.lua	Wed Jan 07 00:57:02 2009 +0000
+++ b/ReAction.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -183,13 +183,6 @@
 
   function CallModuleMethod(modulename, method, ...)
     local m = self:GetModule(modulename,true)
-    if not m then
-      LoadAddOn(("ReAction_%s"):format(modulename))
-      m = self:GetModule(modulename,true)
-      if m then
-        dbprint(("succesfully loaded LOD module: %s"):format(modulename))
-      end
-    end
     if m then
       if type(m) == "table" and type(m[method]) == "function" then
         m[method](m,...)
--- a/ReAction.xml	Wed Jan 07 00:57:02 2009 +0000
+++ b/ReAction.xml	Thu Jan 08 00:57:27 2009 +0000
@@ -6,12 +6,7 @@
 <Include file="locale\locale.xml"/>
 
 <Script file="ReAction.lua"/>
-<Script file="Bar.lua"/>
-<Script file="Overlay.lua"/>
-<Script file="State.lua"/>
-<Script file="LDB.lua"/>
 
 <Include file="modules\modules.xml"/>
-<Include file="bindings.lua"/>
 
 </Ui>
--- a/State.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1338 +0,0 @@
---[[
-  ReAction bar state driver interface
-
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local format = string.format
-local InCombatLockdown = InCombatLockdown
-local RegisterStateDriver = RegisterStateDriver
-
-ReAction:UpdateRevision("$Revision$")
-
--- module declaration
-local moduleID = "State"
-local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
-
--- Utility --
-
--- traverse a table tree by key list and fetch the result or first nil
-local function tfetch(t, ...)
-  for i = 1, select('#', ...) do
-    t = t and t[select(i, ...)]
-  end
-  return t
-end
-
--- traverse a table tree by key list and build tree as necessary
-local function tbuild(t, ...)
-  for i = 1, select('#', ...) do
-    local key = select(i, ...)
-    if not t[key] then t[key] = { } end
-    t = t[key]
-  end
-  return t
-end
-
--- return a new array of keys of table 't', sorted by comparing 
--- sub-fields (obtained via tfetch) of the table values
-local function fieldsort( t, ... )
-  local r = { }
-  for k in pairs(t) do
-    table.insert(r,k)
-  end
-  local path = { ... }
-  table.sort(r, function(lhs, rhs)
-     local olhs = tfetch(t[lhs], unpack(path)) or 0
-     local orhs = tfetch(t[rhs], unpack(path)) or 0
-     return olhs < orhs
-    end)
-  return r
-end
-
--- set a frame-ref, if the frame is valid, or set nil to the
--- corresponding attribute
-local function SetFrameRef(frame, name, refFrame)
-  if refFrame then
-    local _, explicit = refFrame:IsProtected()
-    if not explicit then
-      refFrame = nil
-    end
-  end
-  if refFrame then
-    frame:SetFrameRef(name,refFrame)
-  else
-    frame:SetAttribute("frameref-"..name,nil)
-  end
-end
-
-
-local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll
-
--- PRIVATE --
-do
-
-  -- the field names must match the field names of the options table, below
-  -- the field values are secure snippets or 'true' to skip the snippet for that property.
-  local properties = { 
-    hide = 
-      [[
-        local h = hide and hide[state] and not showAll
-        if h ~= hidden then
-          if h then
-            self:Hide()
-          else
-            self:Show()
-          end
-          hidden = h
-        end
-        if showAll then
-          control:CallMethod("UpdateHiddenLabel", hide and hide[state])
-        end
-      ]],
-
-    --keybindState  TODO: broken
-
-    anchorEnable = 
-      [[
-        local old_anchor = anchorstate
-        anchorstate = (anchorEnable and anchorEnable[state]) and state
-        if old_anchor ~= anchorstate or not set_state then
-          if anchorstate and anchorPoint then
-            if anchorPoint[state] then
-              self:ClearAllPoints()
-              local f = self:GetAttribute("frameref-anchor-"..anchorstate)
-              if f then
-                self:SetPoint(anchorPoint[state], f, anchorRelPoint[state], anchorX[state], anchorY[state])
-              end
-            end
-          elseif defaultAnchor and defaultAnchor.point then
-            self:ClearAllPoints()
-            self:SetPoint(defaultAnchor.point, defaultAnchor.frame, 
-                          defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
-          end
-        end
-      ]],
-      -- anchorEnable handles all the other bits
-    anchorFrame = true,
-    anchorPoint = true,
-    anchorRelPoint = true,
-    anchorX = true,
-    anchorY = true,
-
-
-    enableScale = 
-      [[
-        local old_scale = scalestate
-        scalestate = (enableScale and enableScale[state]) and state
-        if old_scale ~= scalestate or not set_state then
-          if scalestate and scale then
-            if scale[state] then
-              self:SetScale(scale[state])
-            end
-          else
-            self:SetScale(1.0)
-          end
-        end
-      ]],
-      -- enableScale handles scale
-    scale = true,
-
-    enableAlpha = 
-      [[
-        local old_alpha = alphastate
-        alphastate = (enableAlpha and enableAlpha[state]) and state
-        if old_alpha ~= alphastate or not set_state then
-          control:CallMethod("UpdateAlpha", alphastate and alpha[state] or defaultAlpha)
-        end
-      ]],
-      -- enableAlpha handles alpha
-    alpha = true,
-  }
-
-  local weak         = { __mode = "k" }
-  local statedrivers = setmetatable( { }, weak )
-  local keybinds     = setmetatable( { }, weak )
-
-  --
-  -- Secure Handler Snippets
-  --
-  local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState
-  do
-    local stateHandler_propInit = 
-    [[
-      propfuncs = table.new()
-      local proplist = self:GetAttribute("prop-func-list")
-      for s in string.gmatch(proplist, "(%w+)") do
-        table.insert(propfuncs, s)
-      end
-    ]]
-
-    local onStateHandler = 
-    -- function _onstate-reaction( self, stateid, newstate )
-    [[
-      set_state = newstate
-
-      local oldState = state
-      state = state_override or set_state or state
-      for i = 1, #propfuncs do
-        control:RunAttribute("func-"..propfuncs[i])
-      end
-      
-      control:ChildUpdate()
-
-      if oldState ~= state then
-        control:CallMethod("StateRefresh", state)
-      end
-    ]]
-
-    local onClickHandler = 
-    -- function OnClick( self, button, down )
-    [[
-      if state_override == button then
-        state_override = nil -- toggle
-      else
-        state_override = button
-      end
-    ]] .. onStateHandler
-
-    local function UpdateAlpha( frame, alpha )
-      if alpha then
-        frame:SetAlpha(alpha)
-      end
-    end
-
-    -- Construct a lua assignment as a code string and execute it within the header
-    -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called
-    -- with four arguments, then it treats 'varname' as an existing global table and
-    -- sets a key-value pair. For a slight efficiency boost, pass the values in as
-    -- attributes and fetch them as attributes from the snippet code, to leverage snippet
-    -- caching.
-    function SetHandlerData( bar, varname, value, key )
-      local f = bar:GetFrame()
-      f:SetAttribute("data-varname",varname)
-      f:SetAttribute("data-value",  value)
-      f:SetAttribute("data-key",    key)
-      f:Execute(
-        [[
-          local name  = self:GetAttribute("data-varname")
-          local value = self:GetAttribute("data-value")
-          local key   = self:GetAttribute("data-key")
-          if name then
-            if key then
-              if not _G[name] then
-                _G[name] = table.new()
-              end
-              _G[name][key] = value
-            else
-              _G[name] = value
-            end
-          end
-        ]])
-    end
-
-    function SetDefaultAnchor( bar )
-      local point, frame, relPoint, x, y = bar:GetAnchor()
-      SetHandlerData(bar, "defaultAnchor", point, "point")
-      SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint")
-      SetHandlerData(bar, "defaultAnchor", x, "x")
-      SetHandlerData(bar, "defaultAnchor", y, "y")
-      SetHandlerData(bar, "defaultAlpha",  bar:GetAlpha())
-
-      local f = bar:GetFrame()
-      f.UpdateAlpha = UpdateAlpha
-      SetFrameRef(f, "defaultAnchor", _G[frame or "UIParent"])
-      f:Execute(
-        [[
-          defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
-        ]])
-    end
-
-    function RefreshState( bar )
-      SetDefaultAnchor(bar)
-      bar:GetFrame():Execute(
-        [[
-          if self:GetAttribute("reaction-refresh") then
-            control:RunAttribute("reaction-refresh")
-          end
-        ]])
-    end
-
-    function SetStateDriver( bar, rule )
-      local f = bar:GetFrame()
-
-      if not f.UpdateHiddenLabel then
-        function f:UpdateHiddenLabel(hide)
-          bar:SetLabelSubtext( hide and L["Hidden"] )
-        end
-      end
-
-      function f:StateRefresh( state )
-        bar:RefreshControls()
-      end
-
-      local props = { }
-      for p, h in pairs(properties) do
-        if type(h) == "string" then
-          table.insert(props,p)
-          f:SetAttribute("func-"..p, h)
-        end
-      end
-      f:SetAttribute("prop-func-list", table.concat(props," "))
-      f:Execute(stateHandler_propInit)
-      f:SetAttribute("reaction-refresh", onStateHandler)
-      
-      if rule and #rule > 0 then
-        f:SetAttribute( "_onstate-reaction", onStateHandler )
-        RegisterStateDriver(f, "reaction", rule)
-        statedrivers[bar] = rule
-      elseif statedrivers[bar] then
-        UnregisterStateDriver(f, "reaction")
-        f:SetAttribute( "_onstate-reaction", nil )
-        statedrivers[bar] = nil
-      end
-    end
-
-    function SetStateKeybind( bar, key, state )
-      local f = bar:GetFrame()
-
-      local kb = keybinds[bar]
-      if kb == nil then
-        if key == nil then
-          -- nothing to do
-          return
-        end
-        kb = { }
-        keybinds[bar] = kb
-      end
-
-      -- clear the old binding, if any
-      if kb[state] then
-        SetOverrideBinding(f, false, kb[state], nil)
-      end
-      kb[state] = key
-
-      if key then
-        f:SetAttribute("_onclick", onClickHandler)
-        SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button
-      end
-    end
-  end
-
-  -- As far as I can tell the macro clauses are NOT locale-specific.
-  local ruleformats = { 
-    stealth       = "stealth",
-    nostealth     = "nostealth",
-    shadowform    = "form:1",
-    noshadowform  = "noform",
-    pet           = "pet",
-    nopet         = "nopet",
-    harm          = "target=target,harm",
-    help          = "target=target,help",
-    notarget      = "target=target,noexists",
-    focusharm     = "target=focus,harm",
-    focushelp     = "target=focus,help",
-    nofocus       = "target=focus,noexists",
-    raid          = "group:raid",
-    party         = "group:party",
-    solo          = "nogroup",
-    combat        = "combat",
-    nocombat      = "nocombat",
-    possess       = "bonusbar:5",
-  }
-
-  -- Have to do these shenanigans instead of hardcoding the stances/forms because the 
-  -- ordering varies if the character is missing a form. For warriors this is rarely
-  -- a problem (c'mon, who actually skips the level 10 def stance quest?) but for druids
-  -- it can be. Some people never bother to do the aquatic form quest until well past
-  -- when they get cat form, and stance 5/6 can be flight, tree, or moonkin depending
-  -- on talents.
-  function InitRules()
-    local forms = { }
-      -- sort by icon since it's locale-independent
-    for i = 1, GetNumShapeshiftForms() do
-      local icon, name, active = GetShapeshiftFormInfo(i)
-      -- if it's the current form, the icon is wrong (Ability_Spell_WispSplode)
-      -- so capture it from the spell info directly
-      if active then
-        local _1, _2
-        _1, _2, icon = GetSpellInfo(name)
-      end
-      forms[icon] = i;
-    end
-      -- use 9 if not found since 9 is never a valid stance/form
-    local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9
-    local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
-    local bear      = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
-    local aquatic   = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
-    local cat       = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
-    local travel    = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
-    local tree      = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9
-    local moonkin   = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
-    local flight    = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
-
-    ruleformats.battle    = "stance:1"
-    ruleformats.defensive = format("stance:%d",defensive)
-    ruleformats.berserker = format("stance:%d",berserker)
-    ruleformats.caster    = format("form:0/%d/%d/%d",aquatic, travel, flight)
-    ruleformats.bear      = format("form:%d",bear)
-    ruleformats.cat       = format("form:%d",cat)
-    ruleformats.tree      = format("form:%d",tree)
-    ruleformats.moonkin   = format("form:%d",moonkin)
-  end
-
-  local function BuildRule(states)
-    local rules = { }
-    local default
-
-    for idx, state in ipairs(fieldsort(states, "rule", "order")) do
-      local c = states[state].rule
-      local type = c.type
-      if type == "default" then
-        default = default or state
-      elseif type == "custom" then
-        if c.custom then
-          -- strip out all spaces from the custom rule
-          table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
-        end
-      elseif type == "any" or type == "all" then
-        if c.values then
-          local clauses = { }
-          for key, value in pairs(c.values) do
-            table.insert(clauses, ruleformats[key])
-          end
-          if #clauses > 0 then
-            local sep = (type == "any") and "][" or ","
-            table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
-          end
-        end
-      end
-    end
-    -- make sure that the default, if any, is last
-    if default then
-      table.insert(rules, default)
-    end
-    return table.concat(rules,";")
-  end
-
-  local function BuildKeybinds( bar, states )
-    for name, state in pairs(states) do
-      local type = tfetch(state, "rule", "type")
-      if type == "keybind" then
-        local key = tfetch(state, "rule", "keybind")
-        SetStateKeybind(bar, key, name)
-      else
-        SetStateKeybind(bar, nil, name) -- this clears an existing keybind
-      end
-    end
-  end
-
-  function GetProperty( bar, state, propname )
-    return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
-  end
-
-  function SetProperty( bar, state, propname, value )
-    local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state)
-    s[propname] = value
-    SetHandlerData(bar, propname, value, state)
-    RefreshState(bar)
-  end
-
-  function RegisterProperty( propname, snippet )
-    properties[propname] = snippet or true
-    for _, bar in ReAction:IterateBars() do
-      local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
-      if states then
-        for name, s in pairs(states) do
-          SetHandlerData(bar, propname, s[propname], name)
-        end
-        SetStateDriver(bar, BuildRule(states))
-        RefreshState(bar)
-      end
-    end
-  end
-
-  function UnregisterProperty( propname )
-    properties[propname] = nil
-    for _, bar in ReAction:IterateBars() do
-      SetHandlerData(bar, propname, nil)
-      SetStateDriver(bar, BuildRule(states))
-      RefreshState(bar)
-    end
-  end
-
-  function ApplyStates( bar )
-    local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
-    if states then
-      for propname in pairs(properties) do
-        for name, s in pairs(states) do
-          if propname == "anchorFrame" then
-            SetFrameRef(bar:GetFrame(), "anchor-"..name, _G[s.anchorFrame])
-          else
-            SetHandlerData(bar, propname, s[propname], name)
-          end
-        end
-      end
-      BuildKeybinds(bar, states)
-      SetHandlerData(bar, "showAll", ReAction:GetConfigMode())
-      SetStateDriver(bar, BuildRule(states))
-      RefreshState(bar)
-    end
-  end
-
-  function CleanupStates( bar )
-    SetStateDriver(bar, nil)
-  end
-
-  function ShowAll( bar, show )
-    if statedrivers[bar] then
-      SetHandlerData(bar, "showAll", show)
-      RefreshState(bar)
-    end
-  end
-end
-
-
-
--- module event handlers --
-
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID, 
-    {
-      profile = { 
-        bars = { },
-      }
-    }
-  )
-
-  self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
-
-  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
-
-  ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnDestroyBar")
-  ReAction.RegisterCallback(self, "OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnEraseBar")
-  ReAction.RegisterCallback(self, "OnRenameBar")
-  ReAction.RegisterCallback(self, "OnConfigModeChanged")
-end
-
-function module:OnEnable()
-  self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui
-end
-
-function module:UPDATE_SHAPESHIFT_FORMS()
-  -- Re-parse the rules table according to the new form list.
-  -- This happens both at initial login (after PLAYER_ENTERING_WORLD)
-  -- as well as when gaining new abilities. 
-  InitRules()
-  for name, bar in ReAction:IterateBars() do
-    self:OnRefreshBar(nil,bar,name)
-  end
-end
-
-function module:OnRefreshBar(event, bar, name)
-  local c = self.db.profile.bars[name]
-  if c then
-    ApplyStates(bar)
-  end
-end
-
-function module:OnDestroyBar(event, bar, name)
-  CleanupStates(bar)
-end
-
-function module:OnEraseBar(event, bar, name)
-  self.db.profile.bars[name] = nil
-end
-
-function module:OnRenameBar(event, bar, oldname, newname)
-  local bars = self.db.profile.bars
-  bars[newname], bars[oldname] = bars[oldname], nil
-end
-
-function module:OnConfigModeChanged(event, mode)
-  for name, bar in ReAction:IterateBars() do
-    if self.db.profile.bars[name] then
-      ShowAll(bar, mode)
-    end
-  end
-end
-
-
-
--- Options --
-
-local CreateBarOptions, RegisterPropertyOptions
-do
-  local playerClass = select(2, UnitClass("player"))
-  local function ClassCheck(...)
-    for i = 1, select('#',...) do
-      if playerClass == select(i,...) then
-        return false
-      end
-    end
-    return true
-  end
-
-  -- pre-sorted by the order they should appear in
-  local rules = {
-    --  rule          hidden                          fields
-    { "stance",  ClassCheck("WARRIOR"),          { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
-    { "form",    ClassCheck("DRUID"),            { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } },
-    { "stealth", ClassCheck("ROGUE","DRUID"),    { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
-    { "shadow",  ClassCheck("PRIEST"),           { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
-    { "pet",     ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
-    { "target",  false,                          { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
-    { "focus",   false,                          { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
-    { "possess", false,                          { {possess = L["Mind Control"]} } },
-    { "group",   false,                          { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
-    { "combat",  false,                          { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
-  }
-
-  local ruleSelect = { }
-  local ruleMap    = { }
-  local optionMap  = setmetatable({},{__mode="k"})
-
-  local pointTable = {
-    NONE        = " ",
-    CENTER      = L["Center"], 
-    LEFT        = L["Left"],
-    RIGHT       = L["Right"],
-    TOP         = L["Top"],
-    BOTTOM      = L["Bottom"],
-    TOPLEFT     = L["Top Left"],
-    TOPRIGHT    = L["Top Right"],
-    BOTTOMLEFT  = L["Bottom Left"],
-    BOTTOMRIGHT = L["Bottom Right"],
-  }
-
-  -- unpack rules table into ruleSelect and ruleMap
-  for _, c in ipairs(rules) do
-    local rule, hidden, fields = unpack(c)
-    if not hidden then
-      for _, field in ipairs(fields) do
-        local key, label = next(field)
-        table.insert(ruleSelect, label)
-        table.insert(ruleMap, key)
-      end
-    end
-  end
-
-  local stateOptions = {
-    ordering = {
-      name = L["Info"],
-      order = 1,
-      type = "group",
-      args = {
-        delete = {
-          name = L["Delete this State"],
-          order = -1,
-          type = "execute",
-          func = "DeleteState",
-        },
-        rename = {
-          name = L["Name"],
-          order = 1,
-          type = "input",
-          get  = "GetName",
-          set  = "SetStateName",
-          pattern = "^%w*$",
-          usage = L["State names must be alphanumeric without spaces"],
-        },
-        ordering = {
-          name = L["Evaluation Order"],
-          desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
-          order = 2,
-          type = "group",
-          inline = true,
-          args = {
-            up = {
-              name  = L["Up"],
-              order = 1,
-              type  = "execute",
-              width = "half",
-              func  = "MoveStateUp",
-            },
-            down = {
-              name  = L["Down"],
-              order = 2,
-              type  = "execute",
-              width = "half",
-              func  = "MoveStateDown",
-            }
-          }
-        }
-      }
-    },
-    properties = {
-      name = L["Properties"],
-      order = 2,
-      type = "group",
-      args = { 
-        desc = {
-          name = L["Set the properties for the bar when in this state"],
-          order = 1,
-          type = "description"
-        },
-        hide = {
-          name = L["Hide Bar"],
-          order = 90,
-          type = "toggle",
-          set  = "SetProp",
-          get  = "GetProp",
-        },
-        --[[ BROKEN
-        keybindState = {
-          name  = L["Override Keybinds"],
-          desc  = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
-          order = 91,
-          type  = "toggle",
-          set   = "SetProp",
-          get   = "GetProp",
-        }, ]]
-        position = {
-          name  = L["Position"],
-          order = 92,
-          type  = "group",
-          inline = true,
-          args = {
-            anchorEnable = {
-              name  = L["Reposition"],
-              order = 1,
-              type  = "toggle",
-              set   = "SetProp",
-              get   = "GetProp",
-            },
-            anchorFrame = {
-              name   = L["Anchor Frame"],
-              order  = 2,
-              type   = "select",
-              values = "GetAnchorFrames",
-              set    = "SetAnchorFrame",
-              get    = "GetAnchorFrame",
-              disabled = "GetAnchorDisabled",
-              hidden = "GetAnchorDisabled",
-            },
-            anchorPoint = {
-              name  = L["Point"],
-              order = 3,
-              type  = "select",
-              values = pointTable,
-              set   = "SetAnchorPointProp",
-              get   = "GetAnchorPointProp",
-              disabled = "GetAnchorDisabled",
-              hidden = "GetAnchorDisabled",
-            },
-            anchorRelPoint = {
-              name  = L["Relative Point"],
-              order = 4,
-              type  = "select",
-              values = pointTable,
-              set   = "SetAnchorPointProp",
-              get   = "GetAnchorPointProp",
-              disabled = "GetAnchorDisabled",
-              hidden = "GetAnchorDisabled",
-            },
-            anchorX = {
-              name  = L["X Offset"],
-              order = 5,
-              type  = "range",
-              min   = -100,
-              max   = 100,
-              step  = 1,
-              set   = "SetProp",
-              get   = "GetProp",
-              disabled = "GetAnchorDisabled",
-              hidden = "GetAnchorDisabled",
-            },
-            anchorY = {
-              name  = L["Y Offset"],
-              order = 6,
-              type  = "range",
-              min   = -100,
-              max   = 100,
-              step  = 1,
-              set   = "SetProp",
-              get   = "GetProp",
-              disabled = "GetAnchorDisabled",
-              hidden = "GetAnchorDisabled",
-            },
-          },
-        },
-        scale = {
-          name  = L["Scale"],
-          order = 93,
-          type  = "group",
-          inline = true,
-          args = {
-            enableScale = {
-              name  = L["Set New Scale"],
-              order = 1,
-              type  = "toggle",
-              set   = "SetProp",
-              get   = "GetProp",
-            },
-            scale = {
-              name  = L["Scale"],
-              order = 2,
-              type  = "range",
-              min   = 0.25,
-              max   = 2.5,
-              step  = 0.05,
-              isPercent = true,
-              set   = "SetProp",
-              get   = "GetScale",
-              disabled = "GetScaleDisabled",
-              hidden = "GetScaleDisabled",
-            },
-          },
-        },
-        alpha = {
-          name  = L["Transparency"],
-          order = 94,
-          type  = "group",
-          inline = true,
-          args = {
-            enableAlpha = {
-              name  = L["Set Transparency"],
-              order = 1,
-              type  = "toggle",
-              set   = "SetProp",
-              get   = "GetProp",
-            },
-            alpha = {
-              name  = L["Transparency"],
-              order = 2,
-              type  = "range",
-              min   = 0,
-              max   = 1,
-              step  = 0.01,
-              bigStep = 0.05,
-              isPercent = true,
-              set   = "SetProp",
-              get   = "GetAlpha",
-              disabled = "GetAlphaDisabled",
-              hidden = "GetAlphaDisabled",
-            },
-          },
-        },
-      },
-      plugins = { }
-    },
-    rules = {
-      name   = L["Rule"],
-      order  = 3,
-      type   = "group",
-      args   = {
-        mode = {
-          name   = L["Select this state"],
-          order  = 2,
-          type   = "select",
-          style  = "radio",
-          values = { 
-            default = L["by default"], 
-            any = L["when ANY of these"], 
-            all = L["when ALL of these"], 
-            custom = L["via custom rule"],
-            keybind = L["via keybinding"],
-          },
-          set    = "SetType",
-          get    = "GetType",
-        },
-        clear = {
-          name     = L["Clear All"],
-          order    = 3,
-          type     = "execute",
-          hidden   = "GetClearAllDisabled",
-          disabled = "GetClearAllDisabled",
-          func     = "ClearAllConditions",
-        },
-        inputs = {
-          name     = L["Conditions"],
-          order    = 4,
-          type     = "multiselect",
-          hidden   = "GetConditionsDisabled",
-          disabled = "GetConditionsDisabled",
-          values   = ruleSelect,
-          set      = "SetCondition",
-          get      = "GetCondition",
-        },
-        custom = {
-          name = L["Custom Rule"],
-          order = 5,
-          type = "input",
-          multiline = true,
-          hidden = "GetCustomDisabled",
-          disabled = "GetCustomDisabled",
-          desc = L["Syntax like macro rules: see preset rules for examples"],
-          set  = "SetCustomRule",
-          get  = "GetCustomRule",
-          validate = "ValidateCustomRule",
-        },
-        keybind = {
-          name = L["Keybinding"],
-          order = 6,
-          inline = true,
-          hidden = "GetKeybindDisabled",
-          disabled = "GetKeybindDisabled",
-          type = "group",
-          args = {
-            desc = {
-              name = L["Invoking a state keybind toggles an override of all other transition rules."],
-              order = 1,
-              type = "description",
-            },
-            keybind = {
-              name = L["State Hotkey"],
-              desc = L["Define an override toggle keybind"],
-              order = 2,
-              type = "keybinding",
-              set  = "SetKeybind",
-              get  = "GetKeybind",
-            },
-          },
-        },
-      },
-    },
-  }
-
-  local handlers = { }
-  local meta = {
-    __index = function(self, key)
-      for _, h in pairs(handlers) do
-        if h[key] then
-          return h[key]
-        end
-      end
-    end,
-  }
-  local StateHandler = setmetatable({ }, meta)
-  local proto        = { __index = StateHandler }
-
-  function RegisterPropertyOptions( field, options, handler )
-    stateOptions.properties.plugins[field] = options
-    handlers[field] = handler
-  end
-
-  function UnregisterPropertyOptions( field )
-    stateOptions.properties.plugins[field] = nil
-    handlers[field] = nil
-  end
-
-  function StateHandler:New( bar, opts )
-    local self = setmetatable(
-      { 
-        bar = bar 
-      }, 
-      proto )
-
-    function self:GetName()
-      return opts.name
-    end
-
-    function self:SetName(name)
-      opts.name = name
-    end
-
-    function self:GetOrder()
-      return opts.order
-    end
-
-    -- get reference to states table: even if the bar
-    -- name changes the states table ref won't
-    self.states = tbuild(module.db.profile.bars, bar:GetName(), "states")
-    self.state  = tbuild(self.states, opts.name)
-
-    opts.order = self:GetRuleField("order")
-    if opts.order == nil then
-      -- add after the highest
-      opts.order = 100
-      for _, state in pairs(self.states) do
-        local x = tonumber(tfetch(state, "rule", "order"))
-        if x and x >= opts.order then
-          opts.order = x + 1
-        end
-      end
-      self:SetRuleField("order",opts.order)
-    end
-
-    return self
-  end
-
-  -- helper methods
-
-  function StateHandler:SetRuleField( key, value, ... )
-    tbuild(self.state, "rule", ...)[key] = value
-  end
-
-  function StateHandler:GetRuleField( ... )
-    return tfetch(self.state, "rule", ...)
-  end
-
-  function StateHandler:FixAll( setkey )
-    -- if multiple selections in the same group are chosen when 'all' is selected,
-    -- keep only one of them. If changing the mode, the first in the fields list will 
-    -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
-    -- it will be retained.
-    local notified = false
-    if self:GetRuleField("type") == "all" then
-      for _, c in ipairs(rules) do
-        local rule, hidden, fields = unpack(c)
-        local once = false
-        if setkey then
-          for idx, field in ipairs(fields) do
-            if next(field) == setkey then
-              once = true
-            end
-          end
-        end
-        for idx, field in ipairs(fields) do
-          local key = next(field)
-          if self:GetRuleField("values",key) then
-            if once and key ~= setkey then
-              self:SetRuleField(key,false,"values")
-              if not setkey and not notified then
-                ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
-                notified = true
-              end
-            end
-            once = true
-          end
-        end
-      end
-    end
-  end
-
-  function StateHandler:GetNeighbors()
-    local before, after
-    for k, v in pairs(self.states) do
-      local o = tonumber(tfetch(v, "rule", "order"))
-      if o and k ~= self:GetName() then
-        local obefore = tfetch(self.states,before,"rule","order")
-        local oafter  = tfetch(self.states,after,"rule","order")
-        if o < self:GetOrder() and (not obefore or obefore < o) then
-          before = k
-        end
-        if o > self:GetOrder() and (not oafter or oafter > o) then
-          after = k
-        end
-      end
-    end
-    return before, after
-  end
-
-  function StateHandler:SwapOrder( a, b )
-    -- do options table
-    local args = optionMap[self.bar].args
-    args[a].order, args[b].order = args[b].order, args[a].order
-    -- do profile
-    a = tbuild(self.states, a, "rule")
-    b = tbuild(self.states, b, "rule")
-    a.order, b.order = b.order, a.order
-  end
-
-  -- handler methods 
-
-  function StateHandler:GetProp( info )
-    -- gets property of the same name as the options arg
-    return GetProperty(self.bar, self:GetName(), info[#info])
-  end
-
-  function StateHandler:SetProp( info, value )
-    -- sets property of the same name as the options arg
-    SetProperty(self.bar, self:GetName(), info[#info], value)
-  end
-
-  function StateHandler:DeleteState()
-    if self.states[self:GetName()] then
-      self.states[self:GetName()] = nil
-      ApplyStates(self.bar)
-    end
-    optionMap[self.bar].args[self:GetName()] = nil
-  end
-
-  function StateHandler:SetStateName(info, value)
-    -- check for existing state name
-    if self.states[value] then
-      ReAction:UserError(format(L["State named '%s' already exists"],value))
-      return
-    end
-    local args = optionMap[self.bar].args
-    local name = self:GetName()
-    self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil
-    self:SetName(value)
-    ApplyStates(self.bar)
-    ReAction:ShowEditor(self.bar, moduleID, value)
-    end
-
-  function StateHandler:MoveStateUp()
-    local before, after = self:GetNeighbors()
-    if before then
-      self:SwapOrder(before, self:GetName())
-      ApplyStates(self.bar)
-    end
-  end
-
-  function StateHandler:MoveStateDown()
-    local before, after = self:GetNeighbors()
-    if after then
-      self:SwapOrder(self:GetName(), after)
-      ApplyStates(self.bar)
-    end
-  end
-
-  function StateHandler:GetAnchorDisabled()
-    return not GetProperty(self.bar, self:GetName(), "anchorEnable")
-  end
-
-  function StateHandler:GetAnchorFrames(info)
-    self._anchorframes = self._anchorframes or { }
-    table.wipe(self._anchorframes)
-
-    table.insert(self._anchorframes, "UIParent")
-    for name, bar in ReAction:IterateBars() do
-      table.insert(self._anchorframes, bar:GetFrame():GetName())
-    end
-    return self._anchorframes
-  end
-
-  function StateHandler:GetAnchorFrame(info)
-    local value = self:GetProp(info)
-    for k,v in pairs(self._anchorframes) do
-      if v == value then
-        return k
-      end
-    end
-  end
-
-  function StateHandler:SetAnchorFrame(info, value)
-    local f = _G[self._anchorframes[value]]
-    if f then
-      SetFrameRef(self.bar:GetFrame(), "anchor-"..self:GetName(), f)
-      self:SetProp(info, f:GetName())
-    end
-  end
-
-  function StateHandler:SetAnchorPointProp(info, value)
-    self:SetProp(info, value ~= "NONE" and value or nil)
-  end
-
-  function StateHandler:GetAnchorPointProp(info)
-    return self:GetProp(info) or "NONE"
-  end
-
-  function StateHandler:GetScale(info)
-    return self:GetProp(info) or 1.0
-  end
-
-  function StateHandler:GetScaleDisabled()
-    return not GetProperty(self.bar, self:GetName(), "enableScale")
-  end
-
-  function StateHandler:GetAlpha(info)
-    return self:GetProp(info) or 1.0
-  end
-
-  function StateHandler:GetAlphaDisabled()
-    return not GetProperty(self.bar, self:GetName(), "enableAlpha")
-  end
-
-  function StateHandler:SetType(info, value)
-    self:SetRuleField("type", value)
-    self:FixAll()
-    ApplyStates(self.bar)
-  end
-
-  function StateHandler:GetType()
-    return self:GetRuleField("type")
-  end
-
-  function StateHandler:GetClearAllDisabled()
-    local t = self:GetRuleField("type")
-    return not( t == "any" or t == "all" or t == "custom")
-  end
-
-  function StateHandler:ClearAllConditions()
-    local t = self:GetRuleField("type")
-    if t == "custom" then
-      self:SetRuleField("custom","")
-    elseif t == "any" or t == "all" then
-      self:SetRuleField("values", {})
-    end
-    ApplyStates(self.bar)
-  end
-
-  function StateHandler:GetConditionsDisabled()
-    local t = self:GetRuleField("type")
-    return not( t == "any" or t == "all")
-  end
-
-  function StateHandler:SetCondition(info, key, value)
-    self:SetRuleField(ruleMap[key], value or nil, "values")
-    if value then
-      self:FixAll(ruleMap[key])
-    end
-    ApplyStates(self.bar)
-  end
-
-  function StateHandler:GetCondition(info, key)
-    return self:GetRuleField("values", ruleMap[key]) or false
-  end
-
-  function StateHandler:GetCustomDisabled()
-    return self:GetRuleField("type") ~= "custom"
-  end
-
-  function StateHandler:SetCustomRule(info, value)
-    self:SetRuleField("custom",value)
-    ApplyStates(self.bar)
-  end
-
-  function StateHandler:GetCustomRule()
-    return self:GetRuleField("custom") or ""
-  end
-
-  function StateHandler:ValidateCustomRule(info, value)
-    local s = value:gsub("%s","") -- remove all spaces
-    -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
-    repeat
-      if s == "" then
-        return true
-      end
-      local c, r = s:match("(%b[])(.*)")
-      if c == nil and s and #s > 0 then
-        return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "")
-      end
-      s = r
-    until c == nil
-    return true
-  end
-
-  function StateHandler:GetKeybindDisabled()
-    return self:GetRuleField("type") ~= "keybind"
-  end
-
-  function StateHandler:GetKeybind()
-    return self:GetRuleField("keybind")
-  end
-
-  function StateHandler:SetKeybind(info, value)
-    if value and #value == 0 then
-      value = nil
-    end
-    self:SetRuleField("keybind",value)
-    ApplyStates(self.bar)
-  end
-
-  local function CreateStateOptions(bar, name)
-    local opts = { 
-      type = "group",
-      name = name,
-      childGroups = "tab",
-      args = stateOptions
-    }
-
-    opts.handler = StateHandler:New(bar,opts)
-
-    return opts
-  end
-
-  function module:GetBarOptions(bar)
-    local private = { }
-    local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
-    local options = {
-      name = L["Dynamic State"],
-      type = "group",
-      order = -1,
-      childGroups = "tree",
-      disabled = InCombatLockdown,
-      args = {
-        __desc__ = {
-          name = L["States are evaluated in the order they are listed"],
-          order = 1,
-          type = "description",
-        },
-        __new__ = {
-          name = L["New State..."],
-          order = 2,
-          type = "group",
-          args = {
-            name = {
-              name = L["State Name"],
-              desc = L["Set a name for the new state"],
-              order = 1,
-              type = "input",
-              get = function() return private.newstatename or "" end,
-              set = function(info,value) private.newstatename = value end,
-              pattern = "^%w*$",
-              usage = L["State names must be alphanumeric without spaces"],
-            },
-            create = {
-              name = L["Create State"],
-              order = 2,
-              type = "execute",
-              func = function ()
-                  local name = private.newstatename
-                  if states[name] then
-                    ReAction:UserError(format(L["State named '%s' already exists"],name))
-                  else
-                    -- TODO: select default state options and pass as final argument
-                    states[name] = { }
-                    optionMap[bar].args[name] = CreateStateOptions(bar,name)
-                    ReAction:ShowEditor(bar, moduleID, name)
-                    private.newstatename = ""
-                  end
-                end,
-              disabled = function()
-                  local name = private.newstatename or ""
-                  return #name == 0 or name:find("%W")
-                end,
-            }
-          }
-        }
-      }
-    }
-    for name, config in pairs(states) do
-      options.args[name] = CreateStateOptions(bar,name)
-    end
-    optionMap[bar] = options
-    return options
-  end
-end
-
--- Module API --
-
--- Pass in a property field-name, an implementation secure snippet, a static options table, and an 
--- optional options handler method-table
---
--- The options table is static, i.e. not bar-specific and should only reference handler method
--- strings (either existing ones or those added via optHandler). The existing options are ordered
--- 90-99. Order #1 is reserved for the heading.
---
--- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable.
--- See above, for existing API. In particular see the properties set up in the New method: self.bar,
--- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp().
---
-function module:RegisterStateProperty( field, snippetHandler, options, optHandler )
-  RegisterProperty(field, snippetHandler)
-  RegisterPropertyOptions(field, options, optHandler)
-end
-
-function module:UnregisterStateProperty( field )
-  UnregisterProperty(field)
-  UnregisterPropertyOptions(field)
-end
-
-
--- Export methods to Bar class --
-
-function ReAction.Bar:GetState()
-  return GetManagedEnvironment(self:GetFrame()).state
-end
-
-ReAction.Bar.GetStateProperty = GetProperty
-ReAction.Bar.SetStateProperty = SetProperty
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Action.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,1157 @@
+--[[
+  ReAction Action button module.
+
+  The button module implements standard action button functionality by wrapping Blizzard's 
+  ActionBarButtonTemplate frame and associated functions.
+
+  It also provides action remapping support for multiple pages and possessed targets
+  (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc).
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local CreateFrame = CreateFrame
+local format = string.format
+local wipe = wipe
+
+ReAction:UpdateRevision("$Revision$")
+
+-- libraries
+local KB = LibStub("LibKeyBound-1.0")
+local LBF -- initialized later
+
+-- module declaration
+local moduleID = "Action"
+local module = ReAction:NewModule( moduleID )
+
+-- Class declarations
+local Button = { }
+local Handle = { }
+local PropHandler = { }
+
+-- Event handlers
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    { 
+      profile = {
+        bars = { },
+      }
+    }
+  )
+  self.handles = setmetatable({ }, weak)
+
+  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
+
+  ReAction.RegisterCallback(self, "OnCreateBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  ReAction.RegisterCallback(self, "OnRenameBar")
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+
+  LBF = LibStub("LibButtonFacade",true)
+
+  KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
+end
+
+function module:OnEnable()
+  ReAction:RegisterBarType(L["Action Bar"], 
+    { 
+      type = moduleID,
+      defaultButtonSize = 36,
+      defaultBarRows = 1,
+      defaultBarCols = 12,
+      defaultBarSpacing = 3
+    }, true)
+  ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler)
+end
+
+function module:OnDisable()
+  ReAction:UnregisterBarType(L["Action Bar"])
+  ReAction:GetModule("State"):UnregisterStateProperty("page")
+end
+
+function module:OnCreateBar(event, bar, name)
+  if bar.config.type == moduleID then
+    local profile = self.db.profile
+    if profile.bars[name] == nil then
+      profile.bars[name] = {
+        buttons = { }
+      }
+    end
+    if self.handles[bar] == nil then
+      self.handles[bar] = Handle:New(bar, profile.bars[name])
+    end
+  end
+end
+
+function module:OnRefreshBar(event, bar, name)
+  if self.handles[bar] then
+    self.handles[bar]:Refresh()
+  end
+end
+
+function module:OnDestroyBar(event, bar, name)
+  if self.handles[bar] then
+    self.handles[bar]:Destroy()
+    self.handles[bar] = nil
+  end
+end
+
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.bars[name] = nil
+end
+
+function module:OnRenameBar(event, bar, oldname, newname)
+  b = self.db.profile.bars
+  b[newname], b[oldname] = b[oldname], nil
+end
+
+function module:OnConfigModeChanged(event, mode)
+  for _, h in pairs(self.handles) do
+    h:SetConfigMode(mode)
+  end
+end
+
+function module:LIBKEYBOUND_ENABLED(evt)
+  for _, h in pairs(self.handles) do
+    h:ShowGrid(true)
+    h:SetKeybindMode(true)
+  end
+end
+
+function module:LIBKEYBOUND_DISABLED(evt)
+  for _, h in pairs(self.handles) do
+    h:ShowGrid(false)
+    h:SetKeybindMode(false)
+  end
+end
+
+
+---- Interface ----
+function module:GetBarOptions(bar)
+  local h = self.handles[bar]
+  if h then
+    return h:GetOptions()
+  end
+end
+
+
+---- Bar Handle ----
+
+do
+  local options = {
+    hideEmpty = {
+      name = L["Hide Empty Buttons"],
+      order = 1,
+      type = "toggle",
+      width = "double",
+      get  = "GetHideEmpty",
+      set  = "SetHideEmpty",
+    },
+    lockButtons = {
+      name = L["Lock Buttons"],
+      desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
+      order = 2,
+      type = "toggle",
+      disabled = "LockButtonsDisabled",
+      get = "GetLockButtons",
+      set = "SetLockButtons",
+    },
+    lockOnlyCombat = {
+      name = L["Only in Combat"],
+      desc = L["Only lock the buttons when in combat"],
+      order = 3,
+      type = "toggle",
+      disabled = "LockButtonsCombatDisabled",
+      get = "GetLockButtonsCombat",
+      set = "SetLockButtonsCombat",
+    },
+    pages = {
+      name  = L["# Pages"],
+      desc  = L["Use the Dynamic State tab to specify page transitions"],
+      order = 4,
+      type  = "range",
+      min   = 1,
+      max   = 10,
+      step  = 1,
+      get   = "GetNumPages",
+      set   = "SetNumPages",
+    },
+    mindcontrol = {
+      name = L["Mind Control Support"],
+      desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."],
+      order = 5,
+      type = "toggle",
+      width = "double",
+      set = "SetMindControl",
+      get = "GetMindControl",
+    },
+    actions = {
+      name   = L["Edit Action IDs"],
+      order  = 6,
+      type   = "group",
+      inline = true,
+      args   = {
+        method = {
+          name   = L["Assign"],
+          order  = 1,
+          type   = "select",
+          width  = "double",
+          values = { [0] = L["Choose Method..."],
+                     [1] = L["Individually"],
+                     [2] = L["All at Once"], },
+          get    = "GetActionEditMethod",
+          set    = "SetActionEditMethod",
+        },
+        rowSelect = {
+          name   = L["Row"],
+          desc   = L["Rows are numbered top to bottom"],
+          order  = 2,
+          type   = "select",
+          width  = "half",
+          hidden = "IsButtonSelectHidden",
+          values = "GetRowList",
+          get    = "GetSelectedRow",
+          set    = "SetSelectedRow",
+        },
+        colSelect = {
+          name   = L["Col"],
+          desc   = L["Columns are numbered left to right"],
+          order  = 3,
+          type   = "select",
+          width  = "half",
+          hidden = "IsButtonSelectHidden",
+          values = "GetColumnList",
+          get    = "GetSelectedColumn",
+          set    = "SetSelectedColumn",
+        },
+        pageSelect = {
+          name   = L["Page"],
+          order  = 4,
+          type   = "select",
+          width  = "half",
+          hidden = "IsPageSelectHidden",
+          values = "GetPageList",
+          get    = "GetSelectedPage",
+          set    = "SetSelectedPage",
+        },
+        single = {
+          name   = L["Action ID"],
+          usage  = L["Specify ID 1-120"],
+          order  = 5,
+          type   = "input",
+          width  = "half",
+          hidden = "IsButtonSelectHidden",
+          get    = "GetActionID",
+          set    = "SetActionID",
+          validate = "ValidateActionID",
+        },
+        multi = {
+          name   = L["ID List"],
+          usage  = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"],
+          order  = 6,
+          type   = "input",
+          multiline = true,
+          width  = "double",
+          hidden = "IsMultiIDHidden",
+          get    = "GetMultiID",
+          set    = "SetMultiID",
+          validate = "ValidateMultiID",
+        },
+      },
+    },
+  }
+
+  local weak  = { __mode="k" }
+  local meta = { __index = Handle }
+
+  function Handle:New( bar, config )
+    local self = setmetatable(
+      {
+        bar = bar,
+        config = config,
+        btns = { }
+      }, 
+      meta)
+    
+    if self.config.buttons == nil then
+      self.config.buttons = { }
+    end
+    self:Refresh()
+    self:SetKeybindMode(ReAction:GetKeybindMode())
+    return self
+  end
+
+  function Handle:Refresh()
+    local r, c = self.bar:GetButtonGrid()
+    local n = r*c
+    local btnCfg = self.config.buttons
+    if n ~= #self.btns then
+      for i = 1, n do
+        if btnCfg[i] == nil then
+          btnCfg[i] = {}
+        end
+        if self.btns[i] == nil then
+          local b = Button:New(self, i, btnCfg[i], self.config)
+          self.btns[i] = b
+          self.bar:AddButton(i,b)
+        end
+      end
+      for i = n+1, #self.btns do
+        if self.btns[i] then
+          self.bar:RemoveButton(self.btns[i])
+          self.btns[i]:Destroy()
+          self.btns[i] = nil
+          btnCfg[i] = nil
+        end
+      end
+    end
+    local f = self.bar:GetFrame()
+    for _, b in ipairs(self.btns) do
+      b:Refresh()
+    end
+    f:SetAttribute("mindcontrol",self.config.mindcontrol)
+    f:Execute(
+      [[
+      doMindControl = self:GetAttribute("mindcontrol")
+      control:ChildUpdate()
+      ]])
+
+    f:SetAttribute("_onstate-mindcontrol",
+      -- function _onstate-mindcontrol(self, stateid, newstate)
+      [[
+        control:ChildUpdate()
+      ]])
+    RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none")
+    self:UpdateButtonLock()
+  end
+
+  function Handle:Destroy()
+    for _,b in pairs(self.btns) do
+      if b then
+        b:Destroy()
+      end
+    end
+  end
+
+  function Handle:SetConfigMode(mode)
+    for _, b in pairs(self.btns) do
+      b:ShowGrid(mode)
+      b:ShowActionIDLabel(mode)
+    end
+  end
+
+  function Handle:ShowGrid(show)
+    for _, b in pairs(self.btns) do
+      b:ShowGrid(show)
+    end
+  end
+
+  function Handle:UpdateButtonLock()
+    local f = self.bar:GetFrame()
+    f:SetAttribute("lockbuttons",self.config.lockButtons)
+    f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat)
+    f:Execute(
+      [[
+        lockButtons = self:GetAttribute("lockbuttons")
+        lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
+      ]])
+  end
+
+  function Handle:SetKeybindMode(mode)
+    for _, b in pairs(self.btns) do
+      if mode then
+        -- set the border for all buttons to the keybind-enable color
+      	b.border:SetVertexColor(KB:GetColorKeyBoundMode())
+        b.border:Show()
+      elseif IsEquippedAction(b:GetActionID()) then
+        b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua
+      else
+        b.border:Hide()
+      end
+    end
+  end
+
+  function Handle:GetLastButton()
+    return self.btns[#self.btns]
+  end
+
+    -- options handlers
+  function Handle:GetOptions()
+    return {
+      type = "group",
+      name = L["Action Buttons"],
+      handler = self,
+      args = options
+    }
+  end
+
+  function Handle:SetHideEmpty(info, value)
+    if value ~= self.config.hideEmpty then
+      self.config.hideEmpty = value
+      self:ShowGrid(not value)
+    end
+  end
+
+  function Handle:GetHideEmpty()
+    return self.config.hideEmpty
+  end
+
+  function Handle:GetLockButtons()
+    return LOCK_ACTIONBAR == "1" or self.config.lockButtons
+  end
+
+  function Handle:SetLockButtons(info, value)
+    self.config.lockButtons = value
+    self:UpdateButtonLock()
+  end
+
+  function Handle:LockButtonsDisabled()
+    return LOCK_ACTIONBAR == "1"
+  end
+
+  function Handle:GetLockButtonsCombat()
+    return self.config.lockButtonsCombat
+  end
+
+  function Handle:SetLockButtonsCombat(info, value)
+    self.config.lockButtonsCombat = value
+    self:UpdateButtonLock()
+  end
+
+  function Handle:LockButtonsCombatDisabled()
+    return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
+  end
+
+  function Handle:GetNumPages()
+    return self.config.nPages
+  end
+
+  function Handle:SetNumPages(info, value)
+    self.config.nPages = value
+    self:Refresh()
+  end
+
+  function Handle:GetMindControl()
+    return self.config.mindcontrol
+  end
+
+  function Handle:SetMindControl(info, value)
+    self.config.mindcontrol = value
+    self:Refresh()
+  end
+
+  function Handle:GetActionEditMethod()
+    return self.editMethod or 0
+  end
+
+  function Handle:SetActionEditMethod(info, value)
+    self.editMethod = value
+  end
+
+  function Handle:IsButtonSelectHidden()
+    return self.editMethod ~= 1
+  end
+
+  function Handle:GetRowList()
+    local r,c = self.bar:GetButtonGrid()
+    if self.rowList == nil or #self.rowList ~= r then
+      local list = { }
+      for i = 1, r do
+        table.insert(list,i)
+      end
+      self.rowList = list
+    end
+    return self.rowList
+  end
+
+  function Handle:GetSelectedRow()
+    local r, c = self.bar:GetButtonGrid()
+    local row = self.selectedRow or 1
+    if row > r then
+      row = 1
+    end
+    self.selectedRow = row
+    return row
+  end
+
+  function Handle:SetSelectedRow(info, value)
+    self.selectedRow = value
+  end
+
+  function Handle:GetColumnList()
+    local r,c = self.bar:GetButtonGrid()
+    if self.columnList == nil or #self.columnList ~= c then
+      local list = { }
+      for i = 1, c do
+        table.insert(list,i)
+      end
+      self.columnList = list
+    end
+    return self.columnList
+  end
+
+  function Handle:GetSelectedColumn()
+    local r, c = self.bar:GetButtonGrid()
+    local col = self.selectedColumn or 1
+    if col > c then
+      col = 1
+    end
+    self.selectedColumn = col
+    return col
+  end
+
+  function Handle:SetSelectedColumn(info, value)
+    self.selectedColumn = value
+  end
+
+  function Handle:IsPageSelectHidden()
+    return self.editMethod ~= 1 or (self.config.nPages or 1) < 2
+  end
+
+  function Handle:GetPageList()
+    local n = self.config.nPages or 1
+    if self.pageList == nil or #self.pageList ~= n then
+      local p = { }
+      for i = 1, n do
+        table.insert(p,i)
+      end
+      self.pageList = p
+    end
+    return self.pageList
+  end
+
+  function Handle:GetSelectedPage()
+    local p = self.selectedPage or 1
+    if p > (self.config.nPages or 1) then
+      p = 1
+    end
+    self.selectedPage = p
+    return p
+  end
+
+  function Handle:SetSelectedPage(info, value)
+    self.selectedPage = value
+  end
+
+  function Handle:GetActionID()
+    local row = self.selectedRow or 1
+    local col = self.selectedColumn or 1
+    local r, c = self.bar:GetButtonGrid()
+    local n = (row-1) * c + col
+    local btn = self.btns[n]
+    if btn then
+      return tostring(btn:GetActionID(self.selectedPage or 1))
+    end
+  end
+
+  function Handle:SetActionID(info, value)
+    local row = self.selectedRow or 1
+    local col = self.selectedColumn or 1
+    local r, c = self.bar:GetButtonGrid()
+    local n = (row-1) * c + col
+    local btn = self.btns[n]
+    if btn then
+      btn:SetActionID(tonumber(value), self.selectedPage or 1)
+    end
+  end
+
+  function Handle:ValidateActionID(info, value)
+    value = tonumber(value)
+    if value == nil or value < 1 or value > 120 then
+      return L["Specify ID 1-120"]
+    end
+    return true
+  end
+
+  function Handle:IsMultiIDHidden()
+    return self.editMethod ~= 2
+  end
+
+  function Handle:GetMultiID()
+    local p = { }
+    for i = 1, self.config.nPages or 1 do
+      local b = { }
+      for _, btn in ipairs(self.btns) do
+        table.insert(b, btn:GetActionID(i))
+      end
+      table.insert(p, table.concat(b,","))
+    end
+    return table.concat(p,";\n")
+  end
+
+
+  local function ParseMultiID(nBtns, nPages, s)
+    if s:match("[^%d%s,;]") then
+      ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s)
+      return nil
+    end
+    local p = { }
+    for list in s:gmatch("[^;]+") do
+      local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns))
+      local ids = { list:match(pattern) }
+      if #ids ~= nBtns then
+        ReAction:Print("found",#ids,"buttons instead of",nBtns)
+        return nil
+      end
+      table.insert(p,ids)
+    end
+    if #p ~= nPages then
+      ReAction:Print("found",#p,"pages instead of",nPages)
+      return nil
+    end
+    return p
+  end
+
+  function Handle:SetMultiID(info, value)
+    local p = ParseMultiID(#self.btns, self.config.nPages or 1, value)
+    for page, b in ipairs(p) do
+      for button, id in ipairs(b) do
+        self.btns[button]:SetActionID(id, page)
+      end
+    end
+  end
+
+  function Handle:ValidateMultiID(info, value)
+    local bad = L["Invalid action ID list string"]
+    if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then
+      return bad
+    end
+    return true
+  end
+end
+
+
+------ State property options ------
+do
+  local pageOptions = {
+    page = {
+      name     = L["Show Page #"],
+      order    = 11,
+      type     = "select",
+      width    = "half",
+      disabled = "IsPageDisabled",
+      hidden   = "IsPageHidden",
+      values   = "GetPageValues",
+      set      = "SetProp",
+      get      = "GetPage",
+    },
+  }
+
+  local function GetBarConfig(bar)
+    return module.db.profile.bars[bar:GetName()]
+  end
+
+  function PropHandler.GetOptions()
+    return pageOptions
+  end
+
+  function PropHandler:IsPageDisabled()
+    local c = GetBarConfig(self.bar)
+    local n = c and c.nPages or 1
+    return not (n > 1)
+  end
+
+  function PropHandler:IsPageHidden()
+    return not GetBarConfig(self.bar)
+  end
+
+  function PropHandler:GetPageValues()
+    if not self._pagevalues then
+      self._pagevalues = { }
+    end
+    local c = GetBarConfig(self.bar)
+    if c then
+      local n = c.nPages
+        -- cache the results
+      if self._npages ~= n then
+        self._npages = n
+        wipe(self._pagevalues)
+        for i = 1, n do
+          self._pagevalues["page"..i] = i
+        end
+      end
+    end
+    return self._pagevalues
+  end
+
+  function PropHandler:GetPage(info)
+    return self:GetProp(info) or 1
+  end
+
+end
+
+------ ActionID allocation ------
+-- this needs to be high performance when requesting new IDs,
+-- or certain controls will become sluggish. However, the new-request
+-- infrastructure can be built lazily the first time that a new request
+-- comes in (which will only happen at user config time: at static startup
+-- config time all actionIDs should already have been assigned and stored
+-- in the config file)
+
+local IDAlloc
+do
+  local n = 120
+  
+  IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end})
+
+  function IDAlloc:Acquire(id, hint)
+    id = tonumber(id)
+    hint = tonumber(hint)
+    if id and (id < 1 or id > n) then
+      id = nil
+    end
+    if hint and (hint < 1 or hint > n) then
+      hint = nil
+    end
+    if id == nil then
+      -- get a free ID
+      if hint and self[hint] == 0 then
+        -- use the hint if it's free
+        id = hint
+      elseif self.freecount > 0 then
+        -- if neither the id nor the hint are defined or free, but
+        -- the list is known to have free IDs, then start searching
+        -- at the hint for a free one
+        for i = hint or 1, n do
+          if self[i] == 0 then
+            id = i
+            break
+          end
+        end
+        -- self.wrap the search
+        if id == nil and hint and hint > 1 then
+          for i = 1, hint - 1 do
+            if self[i] == 0 then
+              id = i
+              break
+            end
+          end
+        end
+      end
+      if id == nil then
+        -- if there are no free IDs, start wrapping at 1
+        id = self.wrap
+        self.wrap = id + 1
+        if self.wrap > n then
+          self.wrap = 1
+        end
+      end
+    end
+    if self[id] == 0 then
+      self.freecount = self.freecount - 1
+    end
+    self[id] = self[id] + 1
+    return id
+  end
+
+  function IDAlloc:Release(id)
+    id = tonumber(id)
+    if id and (id >= 1 or id <= n) then
+      self[id] = self[id] - 1
+      if self[id] == 0 then
+        self.freecount = self.freecount + 1
+        self.wrap = 1
+      end
+    end
+  end
+end
+
+------ Button class ------
+
+do
+  local frameRecycler = { }
+  local trash = CreateFrame("Frame")
+  local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings
+  do
+    local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
+    local IsActionInRange = IsActionInRange
+
+    local buttonLookup = setmetatable({},{__mode="kv"})
+
+    function OnUpdate(frame, elapsed)
+      -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these
+      --       are only read by ActionButton_OnUpdate (which this function replaces). In
+      --       all other places they're just written, so it doesn't taint any secure code.
+      if frame.flashing == 1 then
+        frame.flashtime = frame.flashtime - elapsed
+        if frame.flashtime <= 0 then
+          local overtime = -frame.flashtime
+          if overtime >= ATTACK_BUTTON_FLASH_TIME then
+            overtime = 0
+          end
+          frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
+
+          local flashTexture = frame.flash
+          if flashTexture:IsShown() then
+            flashTexture:Hide()
+          else
+            flashTexture:Show()
+          end
+        end
+      end
+      
+      if frame.rangeTimer then
+        frame.rangeTimer = frame.rangeTimer - elapsed;
+
+        if frame.rangeTimer <= 0 then
+          if IsActionInRange(frame.action) == 0 then
+            frame.icon:SetVertexColor(1.0,0.1,0.1)
+          else
+            ActionButton_UpdateUsable(frame)
+          end
+          frame.rangeTimer = 0.1
+        end
+      end
+    end
+
+    -- Use KeyBound-1.0 for binding, but use Override bindings instead of
+    -- regular bindings to support multiple profile use. This is a little
+    -- weird with the KeyBound dialog box (which has per-char selector as well
+    -- as an OK/Cancel box) but it's the least amount of effort to implement.
+    function GetActionName(f)
+      local b = buttonLookup[f]
+      if b then
+        return format("%s:%s", b.bar:GetName(), b.idx)
+      end
+    end
+
+    function GetHotkey(f)
+      local b = buttonLookup[f]
+      if b then
+        return KB:ToShortKey(b:GetConfig().hotkey)
+      end
+    end
+
+    function SetKey(f, key)
+      local b = buttonLookup[f]
+      if b then
+        local c = b:GetConfig()
+        if c.hotkey then
+          SetOverrideBinding(f, false, c.hotkey, nil)
+        end
+        if key then
+          SetOverrideBindingClick(f, false, key, f:GetName(), nil)
+        end
+        c.hotkey = key
+        b:DisplayHotkey(GetHotkey(f))
+      end
+    end
+
+    function FreeKey(f, key)
+      local b = buttonLookup[f]
+      if b then
+        local c = b:GetConfig()
+        if c.hotkey == key then
+          local action = f:GetActionName()
+          SetOverrideBinding(f, false, c.hotkey, nil)
+          c.hotkey = nil
+          b:DisplayHotkey(nil)
+          return action
+        end
+      end
+      return ReAction:FreeOverrideHotkey(key)
+    end
+
+    function ClearBindings(f)
+      SetKey(f, nil)
+    end
+
+    function GetBindings(f)
+      local b = buttonLookup[f]
+      if b then
+        return b:GetConfig().hotkey
+      end
+    end
+
+    function KBAttach( button )
+      local f = button:GetFrame()
+      f.GetActionName = GetActionName
+      f.GetHotkey     = GetHotkey
+      f.SetKey        = SetKey
+      f.FreeKey       = FreeKey
+      f.ClearBindings = ClearBindings
+      f.GetBindings   = GetBindings
+      buttonLookup[f] = button
+      f:SetKey(button:GetConfig().hotkey)
+      ReAction:RegisterKeybindFrame(f)
+      if ReAction:GetKeybindMode() then
+      	button.border:SetVertexColor(KB:GetColorKeyBoundMode())
+        button.border:Show()
+      end
+    end
+  end
+
+  local meta = {__index = Button}
+
+  function Button:New( handle, idx, config, barConfig )
+    local bar = handle.bar
+
+    -- create new self
+    self = setmetatable( 
+      { 
+        bar = bar,
+        idx = idx,
+        config = config,
+        barConfig = barConfig,
+      }, meta )
+
+    local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
+    self.name = name
+    config.name = name
+    local lastButton = handle:GetLastButton()
+    config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured
+    self.nPages = 1
+    
+    -- have to recycle frames with the same name: CreateFrame() doesn't overwrite
+    -- existing globals. Can't set to nil in the global because it's then tainted.
+    local parent = bar:GetFrame()
+    local f = frameRecycler[name]
+    if f then
+      f:SetParent(parent)
+    else
+      f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
+      -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
+      -- standard binding. We use override bindings.
+      local hotkey = _G[name.."HotKey"]
+      hotkey:SetParent(trash)
+      hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
+      hotkey:SetWidth(36)
+      hotkey:SetHeight(18)
+      hotkey:SetJustifyH("RIGHT")
+      hotkey:SetJustifyV("TOP")
+      hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
+      f.hotkey = hotkey
+      f.icon = _G[name.."Icon"]
+      f.flash = _G[name.."Flash"]
+      f:SetScript("OnUpdate",OnUpdate)
+    end
+
+    self.hotkey = f.hotkey
+    self.border = _G[name.."Border"]
+
+    f:SetAttribute("action", config.actionID)
+    -- install mind control actions for all buttons just for simplicity
+    if self.idx <= 12 then
+      f:SetAttribute("*action-mc", 120 + self.idx)
+    end
+    
+    -- set a _childupdate handler, called within the header's context
+    f:SetAttribute("_childupdate", 
+      -- function _childupdate(self, snippetid, message)
+      [[
+        local action = "action"
+        if doMindControl and GetBonusBarOffset() == 5 then
+          action = "*action-mc"
+        elseif page and state and page[state] then
+          action = "*action-"..page[state]
+        end
+        local value = self:GetAttribute(action)
+        self:SetAttribute("action",value)
+      ]])
+
+    -- install drag wrappers to lock buttons 
+    bar:GetFrame():WrapScript(f, "OnDragStart",
+      -- OnDragStart(self, button, kind, value, ...)
+      [[
+        if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
+          return "clear"
+        end
+      ]])
+
+    self.frame = f
+
+
+    -- initialize the hide state
+    f:SetAttribute("showgrid",0)
+    self:ShowGrid(not barConfig.hideEmpty)
+    if ReAction:GetConfigMode() then
+      self:ShowGrid(true)
+    end
+
+    -- show the ID label if applicable
+    self:ShowActionIDLabel(ReAction:GetConfigMode())
+
+    -- attach the keybinder
+    KBAttach(self)
+
+    -- attach to skinner
+    bar:SkinButton(self,
+      {
+        HotKey = self.hotkey,
+      }
+    )
+
+    self:Refresh()
+    return self
+  end
+
+  function Button:Destroy()
+    local f = self.frame
+    f:UnregisterAllEvents()
+    f:Hide()
+    f:SetParent(UIParent)
+    f:ClearAllPoints()
+    if self.name then
+      frameRecycler[self.name] = f
+    end
+    if self.config.actionID then
+      IDAlloc:Release(self.config.actionID)
+    end
+    if self.config.pageactions then
+      for _, id in ipairs(self.config.pageactions) do
+        IDAlloc:Release(id)
+      end
+    end
+    self.frame = nil
+    self.config = nil
+    self.bar = nil
+  end
+
+  function Button:Refresh()
+    local f = self.frame
+    self.bar:PlaceButton(self, 36, 36)
+    self:RefreshPages()
+  end
+
+  function Button:GetFrame()
+    return self.frame
+  end
+
+  function Button:GetName()
+    return self.name
+  end
+
+  function Button:GetConfig()
+    return self.config
+  end
+
+  function Button:GetActionID(page)
+    if page == nil then
+      -- get the effective ID
+      return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction()
+    else
+      if page == 1 then
+        return self.config.actionID
+      else
+        return self.config.pageactions and self.config.pageactions[page] or self.config.actionID
+      end
+    end
+  end
+
+  function Button:SetActionID( id, page )
+    id = tonumber(id)
+    page = tonumber(page)
+    if id == nil or id < 1 or id > 120 then
+      error("Button:SetActionID - invalid action ID")
+    end
+    if page and page ~= 1 then
+      if not self.config.pageactions then
+        self.config.pageactions = { }
+      end
+      if self.config.pageactions[page] then
+        IDAlloc:Release(self.config.pageactions[page])
+      end
+      self.config.pageactions[page] = id
+      IDAlloc:Acquire(self.config.pageactions[page])
+      self.frame:SetAttribute(("*action-page%d"):format(page),id)
+    else
+      IDAlloc:Release(self.config.actionID)
+      self.config.actionID = id
+      IDAlloc:Acquire(self.config.actionID)
+      self.frame:SetAttribute("action",id)
+      if self.config.pageactions then
+        self.config.pageactions[1] = id
+        self.frame:SetAttribute("*action-page1",id)
+      end
+    end
+  end
+
+  function Button:RefreshPages( force )
+    local nPages = self.barConfig.nPages
+    if nPages and (nPages ~= self.nPages or force) then
+      local f = self:GetFrame()
+      local c = self.config.pageactions
+      if nPages > 1 and not c then
+        c = { }
+        self.config.pageactions = c
+      end
+      for i = 1, nPages do
+        if i > 1 then
+          c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons())
+        else
+          c[i] = self.config.actionID  -- page 1 is the same as the base actionID
+        end
+        f:SetAttribute(("*action-page%d"):format(i),c[i])
+      end
+      for i = nPages+1, #c do
+        IDAlloc:Release(c[i])
+        c[i] = nil
+        f:SetAttribute(("*action-page%d"):format(i),nil)
+      end
+      self.nPages = nPages
+    end
+  end
+
+  function Button:ShowGrid( show )
+    if not InCombatLockdown() then
+      local f = self.frame
+      local count = f:GetAttribute("showgrid")
+      if show then
+        count = count + 1
+      else
+        count = count - 1
+      end
+      if count < 0 then
+        count = 0
+      end
+      f:SetAttribute("showgrid",count)
+
+      if count >= 1 and not f:GetAttribute("statehidden") then
+        if LBF then
+          LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5)
+        else
+          self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5);
+        end
+        f:Show()
+      elseif count < 1 and not HasAction(self:GetActionID()) then
+        f:Hide()
+      end
+    end
+  end
+
+  function Button:ShowActionIDLabel( show )
+    local f = self:GetFrame()
+    if show then
+      local id = self:GetActionID()
+      if not f.actionIDLabel then
+        local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
+        label:SetAllPoints()
+        label:SetJustifyH("CENTER")
+        label:SetShadowColor(0,0,0,1)
+        label:SetShadowOffset(2,-2)
+        f.actionIDLabel = label -- store the label with the frame for recycling
+
+        f:HookScript("OnAttributeChanged", 
+          function(frame, attr, value)
+            if label:IsVisible() and attr:match("action") then
+              label:SetText(tostring(frame.action))
+            end
+          end)
+      end
+      f.actionIDLabel:SetText(tostring(id))
+      f.actionIDLabel:Show()
+    elseif f.actionIDLabel then
+      f.actionIDLabel:Hide()
+    end
+  end
+
+  function Button:DisplayHotkey( key )
+    self.hotkey:SetText(key or "")
+  end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Bar.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,245 @@
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local CreateFrame = CreateFrame
+local floor = math.floor
+local fmod = math.fmod
+local format = string.format
+
+ReAction:UpdateRevision("$Revision$")
+
+local Bar   = { }
+local proto = { __index = Bar }
+local weak  = { __mode = "k" }
+
+ReAction.Bar = Bar -- export to ReAction
+
+function Bar:New( name, config )
+  if type(config) ~= "table" then
+    error("ReAction.Bar: config table required")
+  end
+
+  -- create new self
+  self = setmetatable( 
+    { 
+      config  = config,
+      name    = name,
+      buttons = setmetatable( { }, weak ),
+      width   = config.width or 480,
+      height  = config.height or 40
+    }, 
+    proto )
+  
+  -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
+  -- not mouse-clickable by the user.
+  local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
+  local f = CreateFrame("Button", name and format("ReAction-%s",name), parent,
+                        "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
+  f:SetFrameStrata("MEDIUM")
+  f:SetWidth(self.width)
+  f:SetWidth(self.height)
+  f:SetAlpha(config.alpha or 1.0)
+  f:Show()
+  f:EnableMouse(false)
+  f:SetClampedToScreen(true)
+
+  -- Override the default frame accessor to provide strict read-only access
+  function self:GetFrame()
+    return f
+  end
+
+  self:ApplyAnchor()
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+
+  return self
+end
+
+function Bar:Destroy()
+  local f = self:GetFrame()
+  f:UnregisterAllEvents()
+  ReAction.UnregisterAllCallbacks(self)
+  f:Hide()
+  f:SetParent(UIParent)
+  f:ClearAllPoints()
+end
+
+function Bar:OnConfigModeChanged(event, mode)
+  self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
+end
+
+function Bar:ApplyAnchor()
+  local f = self:GetFrame()
+  local c = self.config
+  local p = c.point
+
+  f:SetWidth(c.width)
+  f:SetHeight(c.height)
+  f:ClearAllPoints()
+  
+  if p then
+    local a = f:GetParent()
+    if c.anchor then
+      local bar = ReAction:GetBar(c.anchor)
+      if bar then
+        a = bar:GetFrame()
+      else
+        a = _G[c.anchor]
+      end
+    end
+    local fr = a or f:GetParent()
+    f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
+  else
+    f:SetPoint("CENTER")
+  end
+end
+
+function Bar:SetAnchor(point, frame, relativePoint, x, y)
+  local c = self.config
+  c.point = point or c.point
+  c.anchor = frame or c.anchor
+  c.relpoint = relativePoint or c.relpoint
+  c.x = x or c.x
+  c.y = y or c.y
+  self:ApplyAnchor()
+  ReAction:RefreshBar(self)
+end
+
+function Bar:GetAnchor()
+  local c = self.config
+  return (c.point or "CENTER"), 
+         (c.anchor or self:GetFrame():GetParent():GetName()), 
+         (c.relpoint or c.point or "CENTER"), 
+         (c.x or 0), 
+         (c.y or 0)
+end
+
+function Bar:GetSize()
+  local f = self:GetFrame()
+  return f:GetWidth(), f:GetHeight()
+end
+
+function Bar:SetSize(w,h)
+  local f = self:GetFrame()
+  self.config.width = w
+  self.config.height = h
+  f:SetWidth(w)
+  f:SetHeight(h)
+end
+
+function Bar:GetButtonSize()
+  local w = self.config.btnWidth or 32
+  local h = self.config.btnHeight or 32
+  -- TODO: get from modules?
+  return w,h
+end
+
+function Bar:SetButtonSize(w,h)
+  if w > 0 and h > 0 then
+    self.config.btnWidth = w
+    self.config.btnHeight = h
+  end
+  ReAction:RefreshBar(self)
+end
+
+function Bar:GetButtonGrid()
+  local cfg = self.config
+  local r = cfg.btnRows or 1
+  local c = cfg.btnColumns or 1
+  local s = cfg.spacing or 4
+  return r,c,s
+end
+
+function Bar:GetNumButtons()
+  local r,c = self:GetButtonGrid()
+  return r*c
+end
+
+function Bar:SetButtonGrid(r,c,s)
+  if r > 0 and c > 0 and s > 0 then
+    local cfg = self.config
+    cfg.btnRows = r
+    cfg.btnColumns = c
+    cfg.spacing = s
+  end
+  ReAction:RefreshBar(self)
+end
+
+function Bar:ClipNButtons( n )
+  local cfg = self.config
+  local r = cfg.btnRows or 1
+  local c = cfg.btnColumns or 1
+
+  cfg.btnRows = ceil(n/c)
+  cfg.btnColumns = min(n,c)
+end
+
+function Bar:GetName()
+  return self.name
+end
+
+function Bar:GetFrame()
+  -- this method is included for documentation purposes. It is overridden
+  -- for each object in the :New() method.
+  error("Invalid Bar object: used without initialization")
+end
+
+-- only ReAction:RenameBar() should call this function. Calling from any other
+-- context will desync the bar list in the ReAction class.
+function Bar:SetName(name)
+  self.name = name
+  self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua
+end
+
+function Bar:GetAlpha()
+  return self.config.alpha or 1.0
+end
+
+function Bar:SetAlpha(value)
+  self.config.alpha = value
+  self:GetFrame():SetAlpha(value or 1.0)
+  ReAction:RefreshBar(self)
+end
+
+function Bar:AddButton(idx, button)
+  local f = self:GetFrame()
+
+  -- store in a weak reverse-index array
+  self.buttons[button] = idx
+
+  -- Store a properly wrapped reference to the child frame as an attribute 
+  -- (accessible via "frameref-btn#")
+  f:SetFrameRef(format("btn%d",idx), button:GetFrame())
+end
+
+function Bar:RemoveButton(button)
+  local idx = self.buttons[button]
+  if idx then
+    self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
+    self.buttons[button] = nil
+  end
+end
+
+-- iterator returns button, idx and does NOT iterate in index order
+function Bar:IterateButtons()
+  return pairs(self.buttons)
+end
+
+function Bar:PlaceButton(button, baseW, baseH)
+  local idx = self.buttons[button]
+  if idx then 
+    local r, c, s = self:GetButtonGrid()
+    local bh, bw = self:GetButtonSize()
+    local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
+    local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
+    local scale = bw/baseW
+    local b = button:GetFrame()
+
+    b:ClearAllPoints()
+    b:SetPoint("TOPLEFT",x/scale,y/scale)
+    b:SetScale(scale)
+  end
+end
+
+function Bar:SkinButton()
+  -- does nothing by default
+end
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Bindings.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,7 @@
+local L = ReAction.L
+local _G = _G
+
+_G["BINDING_HEADER_REACTION"]             = L["ReAction"]
+
+_G["BINDING_NAME_REACTION_TOGGLELOCK"]    = L["Toggle ReAction Bar Lock"]
+_G["BINDING_NAME_REACTION_TOGGLEKEYBIND"] = L["ReAction Keybinding Mode"]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Config.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,529 @@
+--[[
+  ReAction Configuration UI module
+
+  Hooks into Blizzard Interface Options AddOns panel
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local AceConfigReg = LibStub("AceConfigRegistry-3.0")
+local AceConfigDialog = LibStub("AceConfigDialog-3.0")
+
+ReAction:UpdateRevision("$Revision$")
+
+-- some constants
+local configName = "ReAction"
+
+-- module declaration
+local moduleID = "ConfigUI"
+local module = ReAction:NewModule( moduleID,
+  "AceEvent-3.0"
+)
+
+-- module methods
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    { 
+      profile = {
+        closeOnLaunch = true,
+        editorCloseOnLaunch = true,
+      }
+    }
+  )
+
+  self:RegisterEvent("PLAYER_REGEN_DISABLED")
+  ReAction.RegisterCallback(self,"OnOptionsRegistered","OnOptionsRefreshed")
+  ReAction.RegisterCallback(self,"OnOptionsRefreshed")
+  self:InitializeOptions()
+end
+
+function module:OnOptionsRefreshed(evt)
+  AceConfigReg:NotifyChange(configName)
+  if self.editor then self.editor:Refresh() end
+end
+
+function module:PLAYER_REGEN_DISABLED()
+  if self.editor then
+    self.editor:Hide()
+  end
+end
+
+function module:OpenConfig()
+  InterfaceOptionsFrame_OpenToCategory(configName)
+end
+
+function module:InitializeOptions()
+  ReAction:RegisterOptions(self, {
+      _launchEditor = {
+        type = "execute",
+        handler = self,
+        name = L["Edit Bars..."],
+        desc = L["Show the ReAction Bar Editor dialogue"],
+        func = function()
+            self:LaunchBarEditor()
+            -- you can't close a dialog in response to an options click, because the end of the 
+            -- handler for all the button events calls lib:Open()
+            -- So, schedule a close on the next OnUpdate
+            if self.db.profile.closeOnLaunch then
+              self.editor.closePending = true
+            end
+          end,
+        order = 2,
+      },
+      _closeThis = {
+        type = "toggle",
+        name = L["Close on Launch"],
+        desc = L["Close the Interface Options window when launching the ReAction Bar Editor"],
+        get  = function() return self.db.profile.closeOnLaunch end,
+        set  = function(info, val) self.db.profile.closeOnLaunch = val end,
+        order = 3,
+      },
+      _keybind = {
+        type = "execute",
+        handler = self,
+        name = L["Key Bindings"],
+        desc = L["Show the keybinding dialogue"],
+        func = function()
+            ReAction:SetKeybindMode(true)
+          end,
+        order = 4,
+      },
+    }, true) -- global
+
+  AceConfigReg:RegisterOptionsTable(configName,ReAction.options)
+  self.frame = AceConfigDialog:AddToBlizOptions(configName, configName)
+  self.frame.obj:SetCallback("default", 
+    function() 
+      ReAction.db:ResetProfile()
+      ReAction:RefreshOptions()
+    end )
+end
+
+
+
+
+-- Bar Editor --
+local function NewEditor()
+  -- private variables
+  local editorName = "ReAction-Editor"
+  local barOptMap = setmetatable({},{__mode="v"})
+  local tmp = { }
+  local pointTable = {
+    CENTER      = L["Center"], 
+    LEFT        = L["Left"],
+    RIGHT       = L["Right"],
+    TOP         = L["Top"],
+    BOTTOM      = L["Bottom"],
+    TOPLEFT     = L["Top Left"],
+    TOPRIGHT    = L["Top Right"],
+    BOTTOMLEFT  = L["Bottom Left"],
+    BOTTOMRIGHT = L["Bottom Right"],
+  }
+
+
+  -- use a local GUI container to work around AceConfigDialog closing
+  -- both the bar editor and the global options when interface options is closed
+  local editor = LibStub("AceGUI-3.0"):Create("Frame")
+  local frame = editor.frame
+  frame:SetClampedToScreen(true)
+  frame:Hide()
+  local old_OnUpdate = frame:GetScript("OnUpdate")
+  frame:SetScript("OnUpdate", function(dt)
+      if old_OnUpdate then
+        old_OnUpdate(dt)
+      end
+      if editor.closePending then
+        InterfaceOptionsFrame:Hide()
+        editor.closePending = false
+      end
+      if editor.selfClosePending then
+        editor:Hide()
+        AceConfigReg:NotifyChange(configName)
+        editor.selfClosePending = false
+      end
+    end )
+  editor:SetCallback("OnClose", 
+    function() 
+      ReAction:SetConfigMode(false)
+    end )
+  AceConfigDialog:SetDefaultSize(editorName, 700, 540)
+  
+
+  local name = ("ReAction - %s"):format(L["Bar Editor"])
+  editor:SetTitle(name)
+  local options = {
+    type = "group",
+    name = name,
+    handler = editor,
+    childGroups = "tree",
+    args = {
+      desc = {
+        type = "description",
+        name = L["Use the mouse to arrange and resize the bars on screen. Tooltips on bars indicate additional functionality."],
+        order = 1
+      },
+      launchConfig = {
+        type = "execute",
+        name = L["Global Config"],
+        desc = L["Opens ReAction global configuration settings panel"],
+        func = function()
+            module:OpenConfig()
+            -- you can't close a dialog in response to an options click, because the end of the 
+            -- handler for all the button events calls lib:Open()
+            -- So, schedule a close on the next OnUpdate
+            if module.db.profile.editorCloseOnLaunch then
+              editor.selfClosePending = true
+            end
+          end,
+        order = 2
+      },
+      closeThis = {
+        type = "toggle",
+        name = L["Close on Launch"],
+        desc = L["Close the Bar Editor when opening the ReAction global Interface Options"],
+        get  = function() return module.db.profile.editorCloseOnLaunch end,
+        set  = function(info, val) module.db.profile.editorCloseOnLaunch = val end,
+        order = 3,
+      },
+      new = {
+        type = "group",
+        name = L["New Bar..."],
+        order = 4,
+        args = { 
+          desc = {
+            type = "description",
+            name = L["Choose a name, type, and initial grid for your new action bar:"],
+            order = 1,
+          },
+          name = {
+            type = "input",
+            name = L["Bar Name"],
+            desc = L["Enter a name for your new action bar"],
+            get  = function() return tmp.barName or "" end,
+            set  = function(info, val) tmp.barName = val end,
+            order = 2,
+          },
+          type = {
+            type = "select",
+            name = L["Button Type"],
+            get  = function() return tmp.barType or ReAction:GetDefaultBarType() or "" end,
+            set  = function(info, val) 
+                     local c = ReAction:GetBarTypeConfig(val)
+                     tmp.barType = val 
+                     tmp.barSize = c.defaultButtonSize or tmp.barSize
+                     tmp.barRows = c.defaultBarRows or tmp.barRows
+                     tmp.barCols = c.defaultBarCols or tmp.barCols
+                     tmp.barSpacing = c.defaultBarSpacing or tmp.barSpacing
+                   end,
+            values = "GetBarTypes",
+            order = 3,
+          },
+          grid = {
+            type = "group",
+            name = L["Button Grid"],
+            inline = true,
+            args = {
+              hdr = {
+                type = "header",
+                name = L["Button Grid"],
+                order = 1,
+              },
+              rows = {
+                type = "range",
+                name = L["Rows"],
+                get  = function() return tmp.barRows or 1 end,
+                set  = function(info, val) tmp.barRows = val end,
+                width = "half",
+                min = 1,
+                max = 32,
+                step = 1,
+                order = 2,
+              },
+              cols = {
+                type = "range",
+                name = L["Columns"],
+                get  = function() return tmp.barCols or 12 end,
+                set  = function(info, val) tmp.barCols = val end,
+                width = "half",
+                min = 1, 
+                max = 32,
+                step = 1,
+                order = 3,
+              },
+              sz = {
+                type = "range",
+                name = L["Size"],
+                get  = function() return tmp.barSize or 36 end,
+                set  = function(info, val) tmp.barSize = val end,
+                width = "half",
+                min = 10,
+                max = 72,
+                step = 1,
+                order = 4,
+              },
+              spacing = {
+                type = "range",
+                name = L["Spacing"],
+                get  = function() return tmp.barSpacing or 3 end,
+                set  = function(info, val) tmp.barSpacing = val end,
+                width = "half",
+                min = 0,
+                max = 24,
+                step = 1,
+                order = 5,
+              }
+            },
+            order = 4
+          },
+          spacer = {
+            type = "header",
+            name = "",
+            width = "full",
+            order = -2
+          },
+          go = {
+            type = "execute",
+            name = L["Create Bar"],
+            func = "CreateBar",
+            order = -1,
+          }
+        }
+      }
+    }
+  }
+  AceConfigReg:RegisterOptionsTable(editorName, options)
+
+  function editor:Open(bar, ...)
+    if not frame:IsVisible() then
+      AceConfigDialog:Open(editorName,self)
+    end
+    if bar then
+      AceConfigDialog:SelectGroup(editorName, barOptMap[bar:GetName()], ...)
+    end
+  end
+
+  function editor:Refresh()
+    AceConfigReg:NotifyChange(editorName)
+  end
+
+  function editor:CreateBarTree(bar)
+    local name = bar:GetName()
+    -- AceConfig doesn't allow spaces, etc, in arg key names, and they must be
+    -- unique strings. So generate a unique key (it can be whatever) for the bar
+    local args = options.args
+    local key
+    local i = 1
+    repeat
+      key = ("bar%s"):format(i)
+      i = i+1
+    until args[key] == nil
+    barOptMap[name] = key
+    args[key] = {
+      type = "group",
+      name = name,
+      childGroups = "tab",
+      args = {
+        general = {
+          type = "group",
+          name = L["General"],
+          order = 1,
+          args = {
+            name = {
+              type = "input",
+              name = L["Rename Bar"],
+              get  = function() return bar:GetName() end,
+              set  = function(info, value) return ReAction:RenameBar(bar, value) end,
+              order = 1,
+            },
+            delete = {
+              type = "execute",
+              name = L["Delete Bar"],
+              desc = function() return bar:GetName() end,
+              confirm = true,
+              func = function() ReAction:EraseBar(bar) end,
+              order = 2
+            },
+            anchor = {
+              type = "group",
+              name = L["Anchor"],
+              inline = true,
+              args = {
+                frame = {
+                  type = "input",
+                  name = L["Frame"],
+                  desc = L["The frame that the bar is anchored to"],
+                  get  = function() local _, f = bar:GetAnchor(); return f end,
+                  set  = function(info, val) bar:SetAnchor(nil,val) end,
+                  validate = function(info, name) 
+                      if name then
+                        local f = ReAction:GetBar(name)
+                        if f then
+                          return true
+                        else
+                          f = _G[name]
+                          if f and type(f) == "table" and f.IsObjectType and f:IsObjectType("Frame") then
+                            local _, explicit = f:IsProtected()
+                            return explicit
+                          end
+                        end
+                      end
+                      return false
+                    end,
+                  width = "double",
+                  order = 1
+                },
+                point = {
+                  type = "select",
+                  name = L["Point"],
+                  desc = L["Anchor point on the bar frame"],
+                  style = "dropdown",
+                  get  = function() return bar:GetAnchor() end,
+                  set  = function(info, val) bar:SetAnchor(val) end,
+                  values = pointTable,
+                  order = 2,
+                },
+                relativePoint = {
+                  type = "select",
+                  name = L["Relative Point"],
+                  desc = L["Anchor point on the target frame"],
+                  style = "dropdown",
+                  get  = function() local p,f,r = bar:GetAnchor(); return r end,
+                  set  = function(info, val) bar:SetAnchor(nil,nil,val) end,
+                  values = pointTable,
+                  order = 3,
+                },
+                x = {
+                  type = "input",
+                  pattern = "\-?%d+",
+                  name = L["X offset"],
+                  get = function() local p,f,r,x = bar:GetAnchor(); return ("%d"):format(x) end,
+                  set = function(info,val) bar:SetAnchor(nil,nil,nil,val) end,
+                  order = 4
+                },
+                y = {
+                  type = "input",
+                  pattern = "\-?%d+",
+                  name = L["Y offset"],
+                  get = function() local p,f,r,x,y = bar:GetAnchor(); return ("%d"):format(y) end,
+                  set = function(info,val) bar:SetAnchor(nil,nil,nil,nil,val) end,
+                  order = 5
+                },
+              },
+              order = 3
+            },
+            alpha = {
+              type = "range",
+              name = L["Transparency"],
+              get  = function() return bar:GetAlpha() end,
+              set  = function(info, val) bar:SetAlpha(val) end,
+              min = 0, 
+              max = 1,
+              isPercent = true,
+              step = 0.01,
+              bigStep = 0.05,
+              order = 4,
+            },
+          },
+        },
+      }
+    }
+    self:RefreshBarTree(bar)
+  end
+
+  function editor:RefreshBarTree(bar)
+    local key = barOptMap[bar:GetName()]
+    if key and options.args[key] then
+      options.args[key].plugins = ReAction:GenerateBarOptionsTable(bar)
+      AceConfigReg:NotifyChange(editorName)
+    end
+  end
+
+  function editor:OnCreateBar(evt, bar)
+    if not tmp.creating then
+      -- a bit of hack to work around OnCreateBar event handler ordering
+      self:CreateBarTree(bar)
+    end
+  end
+
+  function editor:OnDestroyBar(evt, bar, name)
+    local key = barOptMap[name]
+    if key then
+      options.args[key] = nil
+    end
+    self:Refresh()
+  end
+
+  function editor:OnEraseBar(evt, name)
+    local key = barOptMap[name]
+    barOptMap[name] = nil
+    if key then
+      options.args[key] = nil
+      self:Refresh()
+    end
+  end
+
+  function editor:OnRenameBar(evt, bar, oldname, newname)
+    local key = barOptMap[oldname]
+    barOptMap[oldname], barOptMap[newname] = nil, key
+    if key then
+      options.args[key].name = newname
+      self:Refresh()
+    end
+  end
+  
+  function editor:OnBarOptionGeneratorRegistered(evt)
+    for name in pairs(barOptMap) do
+      local bar = ReAction:GetBar(name)
+      if bar then
+        self:RefreshBarTree(bar)
+      end
+    end
+  end
+
+  local _scratch = { }
+  function editor:GetBarTypes()
+    for k,v in pairs(_scratch) do
+      _scratch[k] = nil
+    end
+    return ReAction:GetBarTypeOptions(_scratch)
+  end
+
+  function editor:CreateBar()
+    if tmp.barName and tmp.barName ~= "" then
+      tmp.creating = true
+      local bar = ReAction:CreateBar(tmp.barName, tmp.barType or ReAction:GetDefaultBarType(), tmp.barRows, tmp.barCols, tmp.barSize, tmp.barSpacing)
+      self:CreateBarTree(bar)
+      AceConfigDialog:SelectGroup(editorName, barOptMap[tmp.barName])
+      tmp.barName = nil
+      tmp.creating = false
+    end
+  end
+
+  ReAction.RegisterCallback(editor,"OnCreateBar")
+  ReAction.RegisterCallback(editor,"OnDestroyBar")
+  ReAction.RegisterCallback(editor,"OnEraseBar")
+  ReAction.RegisterCallback(editor,"OnRenameBar")
+  ReAction.RegisterCallback(editor,"OnBarOptionGeneratorRegistered")
+
+  for name, bar in ReAction:IterateBars() do
+    editor:CreateBarTree(bar)
+  end
+
+  return editor
+end
+
+
+function module:LaunchBarEditor(bar, ...)
+  if InCombatLockdown() then
+    ReAction:UserError(L["ReAction config mode disabled during combat."])
+  else
+    if not self.editor then
+      self.editor = NewEditor()
+    end
+    self.editor:Open(bar, ...)
+    ReAction:SetConfigMode(true)
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/HideBlizzard.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,133 @@
+--[[
+  ReAction 'Hide Blizzard' module
+
+  Hides Blizzard action bars. This hides the extra action bars, stance bar, pet bar, and 
+  main menu bar, which in turn hides the experience bar, bag bar, micro menu bar, and lag meter.
+
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+
+ReAction:UpdateRevision("$Revision$")
+
+-- module declaration
+local moduleID = "HideBlizzard"
+local module = ReAction:NewModule( moduleID )
+
+
+-- module methods
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    { 
+      profile = {
+        hide = false
+      }
+    }
+  )
+  self.db.RegisterCallback(self,"OnProfileChanged")
+  self.db.RegisterCallback(self,"OnProfileCopied", "OnProfileChanged")
+  self.db.RegisterCallback(self,"OnProfileReset",  "OnProfileChanged")
+
+  self.hiddenFrame = CreateFrame("Frame")
+  self.hiddenFrame:Hide()
+
+  ReAction:RegisterOptions(self, {
+      hideBlizzard = {
+        type = "toggle",
+        handler = self,
+        name = L["Hide Blizzard Action Bars"],
+        desc = L["Hide the default main bar and extra action bars"],
+        get  = "IsHidden",
+        set  = function(info,value) self:SetHidden(value) end,
+        disabled = InCombatLockdown
+      },
+    }, true) -- global
+
+end
+
+function module:OnEnable()
+  if self.db.profile.hide then
+    self:HideAll(true)
+  end
+
+  -- reroute blizzard action bar config to ReAction config window
+  InterfaceOptionsActionBarsPanel:HookScript("OnShow", 
+    function() 
+      if module:IsEnabled() and module:IsHidden() then
+        ReAction:ShowConfig()
+      end
+    end )
+end
+
+function module:OnDisable()
+  self:ShowAll(true)
+end
+
+function module:OnProfileChanged()
+  if self.db.profile.hide then
+    module:HideAll(true)
+  else
+    module:ShowAll(true)
+  end
+end
+
+local frames = {
+  MainMenuBar,
+  MultiBarLeft,
+  MultiBarRight,
+  MultiBarBottomLeft,
+  MultiBarBottomRight,
+  -- possess bar frame needs to be pulled out separately: stash its children away
+  PossessBarLeft,
+  PossessBarRight,
+  PossessButton1,
+  PossessButton2
+}
+
+local hidden = { }
+
+function module:HideAll( force )
+  if not(self.db.profile.hide) or force then
+    self.db.profile.hide = true
+    for _, f in pairs(frames) do
+      hidden[f] = hidden[f] or { parent = f:GetParent(), wasShown = f:IsShown() }
+      f:SetParent(self.hiddenFrame)
+      f:Hide()
+    end
+  end
+  PossessBarFrame:SetParent(UIParent)
+  PossessBarFrame:EnableMouse(false)
+end
+
+function module:ShowAll( force )
+  PossessBarFrame:EnableMouse(true)
+  PossessBarFrame:SetParent(MainMenuBar)
+  if self.db.profile.hide or force then
+    self.db.profile.hide = false
+
+    for _, f in pairs(frames) do
+      local h = hidden[f]
+      if h then
+        f:SetParent(h.parent)
+        if h.wasShown then
+          f:Show()
+        end
+      end
+    end
+  end
+end
+
+function module:IsHidden()
+  return self.db.profile.hide
+end
+
+function module:SetHidden(h)
+  if h then
+    self:HideAll()
+  else
+    self:ShowAll()
+  end
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/LBF.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,115 @@
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+
+-- module declaration
+local moduleID = "ButtonFacade"
+local module = ReAction:NewModule( moduleID )
+
+-- handlers
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    {
+      profile = {
+        -- default profile goes here
+      }
+    }
+  )
+
+  local LBF = LibStub("LibButtonFacade",true)
+
+  if not LBF then -- no more initialization
+    return
+  end
+
+  self.LBF = LBF
+  self.groups = { }
+
+  -- override a method of ReAction.Bar
+  -- note that 'self' in this context refers to the bar
+  function ReAction.Bar:SkinButton( button, data )
+    module:GetGroup(self:GetName()):AddButton(button:GetFrame(), data)
+  end
+
+  -- register some common events
+  ReAction.RegisterCallback(self, "OnCreateBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  ReAction.RegisterCallback(self, "OnRenameBar")
+
+  self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self)
+end
+
+function module:OnEnable()
+
+end
+
+function module:OnDisable()
+
+end
+
+function module:OnCreateBar(event, bar, name)
+  local c = self.db.profile[name]
+  if not c then
+     c = { 
+      skinID = "Blizzard",
+      backdrop = true,
+      gloss = 0,
+      colors = {},
+    }
+    self.db.profile[name] = c
+  end
+
+  local g = self:GetGroup(name)
+  g.SkinID   = c.skinID or "Blizzard"
+  g.Backdrop = c.backdrop
+  g.Gloss    = c.gloss
+  g.Colors   = c.colors
+end
+
+function module:OnDestroyBar(event, bar, name)
+  if self.groups[name] then
+    self.groups[name]:Delete()
+    self.groups[name] = nil
+  end
+end
+
+function module:OnRefreshBar(event, bar, name)
+  local c = self.db.profile[name]
+  local g = self.groups[name]
+  if c and g then
+    g:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
+  end
+end
+
+function module:OnEraseBar(event, bar, name)
+  self:OnDestroyBar(event, bar, name)
+  self.db.profile[name] = nil
+end
+
+function module:OnRenameBar(event, bar, oldName, newName)
+  if self.groups[name] then
+    self.groups[name]:Delete(true)
+    self.db.profile[oldName], self.db.profile[newName] = nil, self.db.profile[oldName]
+    self:OnCreateBar(event, bar, newName)
+  end
+end
+
+function module:OnSkinChanged( skinID, gloss, backdrop, group, button, colors )
+  local c = self.db.profile[group]
+  if c then
+    c.skinID   = skinID
+    c.gloss    = gloss
+    c.backdrop = backdrop
+    c.colors   = colors
+  end  
+end
+
+function module:GetGroup( name )
+  if not self.groups[name] then
+    self.groups[name] = self.LBF:Group("ReAction", name)
+  end
+  return self.groups[name]
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/LDB.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,36 @@
+local ReAction = ReAction
+local L = ReAction.L
+local format = string.format
+
+-- Export ReAction launcher to LibDataBroker-aware displays
+LibStub:GetLibrary("LibDataBroker-1.1"):NewDataObject( "ReAction", 
+  {
+    type = "launcher", 
+    icon = "Interface\\Icons\\INV_Qiraj_JewelEncased",
+
+    OnClick = function( frame, button )
+      if not InCombatLockdown() then
+        if IsAltKeyDown() then
+          ReAction:SetKeybindMode( not ReAction:GetKeybindMode() )
+        elseif IsShiftKeyDown() then
+          ReAction:SetConfigMode( not ReAction:GetConfigMode() )
+        elseif button == "RightButton" then
+          ReAction:ShowEditor()
+        else
+          ReAction:ShowConfig()
+        end
+      else
+        ReAction:UserError(L["ReAction: can't configure in combat"])
+      end
+    end,
+
+      -- this isn't included in the 'launcher' type LDB spec but it seems all launcher displays use it
+    OnTooltipShow = function( tooltip )
+      tooltip:AddLine(format("|cffffffff%s|r %s",L["Click"],L["for global configuration"]))
+      tooltip:AddLine(format("|cffffd200%s|r %s",L["Right-click"],L["for bar editor dialog"]))
+      tooltip:AddLine(format("|cff00ff00%s|r %s",L["Shift-click"],L["to unlock bars"]))
+      tooltip:AddLine(format("|cff00cccc%s|r %s",L["Alt-click"],L["for keybind mode"]))
+    end,
+
+  }
+)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/ModuleTemplate.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,73 @@
+--[[
+  ReAction module template
+
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+
+-- module declaration
+local moduleID = "MyModuleName"
+local module = ReAction:NewModule( moduleID,
+  -- mixins go here
+)
+
+-- handlers
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID
+    {
+      profile = {
+        -- default profile goes here
+      }
+    }
+  )
+
+  -- register some common events
+  ReAction.RegisterCallback(self, "OnCreateBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  ReAction.RegisterCallback(self, "OnRenameBar")
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+end
+
+function module:OnEnable()
+
+end
+
+function module:OnDisable()
+
+end
+
+-- apply module features and settings to a bar object (see Bar.lua for Bar API)
+function module:OnCreateBar(event, bar, name)
+
+end
+
+-- remove module features and settings from a bar object
+function module:OnDestroyBar(event, bar, name)
+
+end
+
+-- refresh module features and settings on a bar object
+function module:OnRefreshBar(event, bar, name)
+
+end
+
+-- erase any local configuration entries for the supplied bar name
+function module:OnEraseBar(event, bar, name)
+
+end
+
+-- update any local configuration/option entries with the new bar name index
+function module:OnRenameBar(event, bar, oldName, newName)
+
+end
+
+-- update any local display/options based on config mode (true/false)
+function module:OnConfigModeChanged(event, mode)
+
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/Overlay.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,769 @@
+local ReAction               = ReAction
+local L                      = ReAction.L
+local CreateFrame            = CreateFrame
+local InCombatLockdown       = InCombatLockdown
+local floor                  = math.floor
+local min                    = math.min
+local format                 = string.format
+local GameTooltip            = GameTooltip
+local Bar                    = ReAction.Bar
+local GetSize                = Bar.GetSize
+local SetSize                = Bar.SetSize
+local GetButtonSize          = Bar.GetButtonSize
+local GetButtonGrid          = Bar.GetButtonGrid
+local SetButtonSize          = Bar.SetButtonSize
+local SetButtonGrid          = Bar.SetButtonGrid
+local ApplyAnchor            = Bar.ApplyAnchor
+local GameTooltipTextRight1  = GameTooltipTextRight1
+local GameTooltipTextRight2  = GameTooltipTextRight2
+local GameTooltipTextRight3  = GameTooltipTextRight3
+
+local KB = LibStub("LibKeyBound-1.0")
+
+ReAction:UpdateRevision("$Revision$")
+
+
+--
+-- Wrap some of the bar manipulators to make them state-aware
+--
+local function SetAnchor( bar, point, frame, relPoint, x, y )
+  local state = bar:GetState()
+  if state then
+    local anchorstate = bar:GetStateProperty(state, "anchorEnable")
+    if anchorstate then
+      bar:SetStateProperty(state, "anchorFrame", frame)
+      bar:SetStateProperty(state, "anchorPoint", point)
+      bar:SetStateProperty(state, "anchorRelPoint", relPoint)
+      bar:SetStateProperty(state, "anchorX", x or 0)
+      bar:SetStateProperty(state, "anchorY", y or 0)
+      bar:SetAnchor(bar:GetAnchor())
+      return
+    end
+  end
+  bar:SetAnchor(point, frame, relPoint, x, y)
+end
+
+local function GetStateScale( bar )
+  local state = bar:GetState()
+  if state and bar:GetStateProperty(state, "enableScale") then
+    return bar:GetStateProperty(state, "scale")
+  end
+end
+
+local function SetStateScale( bar, scale )
+  local state = bar:GetState()
+  if state and bar:GetStateProperty(state, "enableScale") then
+    bar:SetStateProperty(state, "scale", scale)
+  end
+end
+
+
+--
+-- Bar config overlay
+--
+
+local function GetNormalTextColor()
+  return 1.0, 1.0, 1.0, 1.0
+end
+
+local function GetAnchoredTextColor()
+  return 1.0, 1.0, 1.0, 1.0
+end
+
+local function GetNormalBgColor()
+  return 0.7, 0.7, 1.0, 0.3
+end
+
+local function GetAnchoredBgColor()
+  return 0.9, 0.2, 0.7, 0.3
+end
+
+local function StoreSize(bar)
+  local f = bar:GetFrame()
+  SetSize( bar, f:GetWidth(), f:GetHeight() )
+end
+
+local function StoreExtents(bar)
+  local f = bar:GetFrame()
+  local p, fr, rp, x, y = f:GetPoint(1)
+  fr = fr and fr:GetName() or "UIParent"
+  SetAnchor( bar, p, fr, rp, x, y )
+  SetSize( bar, f:GetWidth(), f:GetHeight() )
+end
+
+local function RecomputeButtonSize(bar)
+  local w, h = GetSize(bar)
+  local bw, bh = GetButtonSize(bar)
+  local r, c, s = GetButtonGrid(bar)
+
+  local scaleW = (floor(w/c) - s) / bw
+  local scaleH = (floor(h/r) - s) / bh
+  local scale = min(scaleW, scaleH)
+
+  SetButtonSize(bar, scale * bw, scale * bh, s)
+end
+
+local function ComputeBarScale(bar)
+  local w, h = bar.controlFrame:GetWidth() - 8, bar.controlFrame:GetHeight() - 8
+  local bw, bh = GetButtonSize(bar)
+  local r, c, s = GetButtonGrid(bar)
+
+  local scaleW = w / (c*(bw+s))
+  local scaleH = h / (r*(bh+s))
+  local scale = min(scaleW, scaleH)
+
+  if scale > 2.5 then
+    scale = 2.5
+  elseif scale < 0.25 then
+    scale = 0.25
+  end
+
+  return scale
+end
+
+local function RecomputeButtonSpacing(bar)
+  local w, h = GetSize(bar)
+  local bw, bh = GetButtonSize(bar)
+  local r, c, s = GetButtonGrid(bar)
+
+  SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh))
+end
+
+local function RecomputeGrid(bar)
+  local w, h = GetSize(bar)
+  local bw, bh = GetButtonSize(bar)
+  local r, c, s = GetButtonGrid(bar)
+
+  SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s)
+end
+
+local function ClampToButtons(bar)
+  local bw, bh = GetButtonSize(bar)
+  local r, c, s = GetButtonGrid(bar)
+  SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1)
+end
+
+local function HideGameTooltip()
+  GameTooltip:Hide()
+end
+
+local anchorInside  = { inside = true }
+local anchorOutside = { outside = true }
+local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" }
+local oppositeEdges = {
+  TOP = "BOTTOM",
+  BOTTOM = "TOP",
+  LEFT = "RIGHT",
+  RIGHT = "LEFT"
+}
+local pointsOnEdge = {
+  BOTTOM = { "BOTTOM", "BOTTOMLEFT",  "BOTTOMRIGHT",  },
+  TOP    = { "TOP",    "TOPLEFT",     "TOPRIGHT",     },
+  RIGHT  = { "RIGHT",  "BOTTOMRIGHT", "TOPRIGHT",     },
+  LEFT   = { "LEFT",   "BOTTOMLEFT",  "TOPLEFT",      },
+}
+local edgeSelector = {
+  BOTTOM = 1,  -- select x of x,y
+  TOP    = 1,  -- select x of x,y
+  LEFT   = 2,  -- select y of x,y
+  RIGHT  = 2,  -- select y of x,y  
+}
+local snapPoints = {
+  [anchorOutside] = {
+    BOTTOMLEFT  = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"},
+    BOTTOM      = {"TOP"},
+    BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"},
+    RIGHT       = {"LEFT"},
+    TOPRIGHT    = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"},
+    TOP         = {"BOTTOM"},
+    TOPLEFT     = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"},
+    LEFT        = {"RIGHT"},
+    CENTER      = {"CENTER"}
+  },
+  [anchorInside] = {
+    BOTTOMLEFT  = {"BOTTOMLEFT"},
+    BOTTOM      = {"BOTTOM"},
+    BOTTOMRIGHT = {"BOTTOMRIGHT"},
+    RIGHT       = {"RIGHT"},
+    TOPRIGHT    = {"TOPRIGHT"},
+    TOP         = {"TOP"},
+    TOPLEFT     = {"TOPLEFT"},
+    LEFT        = {"LEFT"},
+    CENTER      = {"CENTER"}
+  }
+}
+local insidePointOffsetFuncs = {
+  BOTTOMLEFT  = function(x, y) return x, y end,
+  BOTTOM      = function(x, y) return 0, y end,
+  BOTTOMRIGHT = function(x, y) return -x, y end,
+  RIGHT       = function(x, y) return -x, 0 end,
+  TOPRIGHT    = function(x, y) return -x, -y end,
+  TOP         = function(x, y) return 0, -y end,
+  TOPLEFT     = function(x, y) return x, -y end,
+  LEFT        = function(x, y) return x, 0 end,
+  CENTER      = function(x, y) return x, y end,
+}
+local pointCoordFuncs = {
+  BOTTOMLEFT  = function(f) return f:GetLeft(),  f:GetBottom() end,
+  BOTTOM      = function(f) return nil,          f:GetBottom() end,
+  BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end,
+  RIGHT       = function(f) return f:GetRight(), nil end,
+  TOPRIGHT    = function(f) return f:GetRight(), f:GetTop() end,
+  TOP         = function(f) return nil,          f:GetTop() end,
+  TOPLEFT     = function(f) return f:GetLeft(),  f:GetTop() end,
+  LEFT        = function(f) return f:GetLeft(),  nil end,
+  CENTER      = function(f) return f:GetCenter() end,
+}
+local edgeBoundsFuncs = {
+  BOTTOM = function(f) return f:GetLeft(), f:GetRight() end,
+  LEFT   = function(f) return f:GetBottom(), f:GetTop() end
+}
+edgeBoundsFuncs.TOP   = edgeBoundsFuncs.BOTTOM
+edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT
+local cornerTexCoords = {
+              -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy
+  TOPLEFT     = { 1,   1,   1,   0,   0,   1,   0,   0 },
+  TOPRIGHT    = { 1,   0,   0,   0,   1,   1,   0,   1 },
+  BOTTOMLEFT  = { 0,   1,   1,   1,   0,   0,   1,   0 },
+  BOTTOMRIGHT = { 0,   0,   0,   1,   1,   0,   1,   1 },
+}
+
+-- Returns absolute coordinates x,y of the named point 'p' of frame 'f'
+local function GetPointCoords( f, p )
+  local x, y = pointCoordFuncs[p](f)
+  if not(x and y) then
+    local cx, cy = f:GetCenter()
+    x = x or cx
+    y = y or cy
+  end
+  return x, y
+end
+
+
+-- Returns true if frame 'f1' can be anchored to frame 'f2'
+local function CheckAnchorable( f1, f2 )
+  -- can't anchor a frame to itself or to nil
+  if f1 == f2 or f2 == nil then
+    return false
+  end
+  
+  -- can always anchor to UIParent
+  if f2 == UIParent then
+    return true
+  end
+  
+  -- also can't do circular anchoring of frames 
+  -- walk the anchor chain, which generally shouldn't be that expensive
+  -- (who nests draggables that deep anyway?)
+  for i = 1, f2:GetNumPoints() do
+    local _, f = f2:GetPoint(i)
+    if not f then f = f2:GetParent() end
+    return CheckAnchorable(f1,f)
+  end
+  
+  return true
+end
+
+-- Returns true if frames f1 and f2 specified edges overlap
+local function CheckEdgeOverlap( f1, f2, e )
+  local l1, u1 = edgeBoundsFuncs[e](f1)
+  local l2, u2 = edgeBoundsFuncs[e](f2)
+  return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2
+end
+
+-- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2
+local function CheckPointEdgeOverlap( f1, p1, f2, e2 )
+  local l, u = edgeBoundsFuncs[e2](f2)
+  local x, y = GetPointCoords(f1,p1)
+  x = select(edgeSelector[e2], x, y)
+  return l <= x and x <= u
+end
+
+-- Returns the distance between corresponding edges. It is 
+-- assumed that the passed in edges e1 and e2 are the same or opposites
+local function GetEdgeDistance( f1, f2, e1, e2 )
+  local x1, y1 = pointCoordFuncs[e1](f1)
+  local x2, y2 = pointCoordFuncs[e2](f2)
+  return math.abs((x1 or y1) - (x2 or y2))
+end
+
+local globalSnapTargets = { [UIParent] = anchorInside }
+
+local function GetClosestFrameEdge(f1,f2,a)
+  local dist, edge, opp
+  if f2:IsVisible() and CheckAnchorable(f1,f2) then
+    for _, e in pairs(edges) do
+      local o = a.inside and e or oppositeEdges[e]
+      if CheckEdgeOverlap(f1,f2,e) then
+        local d = GetEdgeDistance(f1, f2, e, o)
+        if not dist or (d < dist) then
+          dist, edge, opp = d, e, o
+        end
+      end
+    end
+  end
+  return dist, edge, opp
+end
+
+local function GetClosestVisibleEdge( f )
+  local r, o, e1, e2
+  local a = anchorOutside
+  for _, b in ReAction:IterateBars() do
+    local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a)
+    if d and (not r or d < r) then
+      r, o, e1, e2 = d, b:GetFrame(), e, opp
+    end
+  end
+  for f2, a2 in pairs(globalSnapTargets) do
+    local d, e, opp = GetClosestFrameEdge(f,f2,a2)
+    if d and (not r or d < r) then
+      r, o, e1, e2, a = d, f2, e, opp, a2
+    end
+  end
+  return o, e1, e2, a
+end
+
+local function GetClosestVisiblePoint(f1)
+  local f2, e1, e2, a = GetClosestVisibleEdge(f1)
+  if f2 then
+    local rsq, p, rp, x, y
+    -- iterate pointsOnEdge in order and use < to prefer edge centers to corners
+    for _, p1 in ipairs(pointsOnEdge[e1]) do
+      if CheckPointEdgeOverlap(f1,p1,f2,e2) then
+        for _, p2 in pairs(snapPoints[a][p1]) do
+          local x1, y1 = GetPointCoords(f1,p1)
+          local x2, y2 = GetPointCoords(f2,p2)
+          local dx = x1 - x2
+          local dy = y1 - y2
+          local rsq2 = dx*dx + dy*dy
+          if not rsq or rsq2 < rsq then
+            rsq, p, rp, x, y = rsq2, p1, p2, dx, dy
+          end
+        end
+      end
+    end
+    return f2, p, rp, x, y
+  end
+end
+
+local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff)
+  local o, p, rp, x, y = GetClosestVisiblePoint(f1)
+  local s = false
+
+  local insideOffsetFunc = p and insidePointOffsetFuncs[p]
+  local coordFunc = p and pointCoordFuncs[p]
+  if not insideOffsetFunc or not coordFunc then
+    return
+  end
+  
+  local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0)
+  local xx, yy = coordFunc(f1)
+  if xx and yy then
+    if math.abs(x) <= rx then
+      if math.abs(y) <= ry then
+        x = sx
+        y = sy
+        s = true
+      elseif CheckEdgeOverlap(f1,o,"LEFT") then
+        x = sx
+        s = true
+      end
+    elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then
+      y = sy
+      s = true
+    end
+  elseif xx then
+    if math.abs(x) <= rx then
+      x = sx
+      s = true
+      if math.abs(y) <= ry then
+        y = sy
+      end
+    end
+  elseif yy then
+    if math.abs(y) <= ry then
+      y = sy
+      s = true
+      if math.abs(x) <= rx then
+        x = sx
+      end
+    end
+  end
+
+  -- correct for some Lua oddities with doubles
+  if x == -0 then x = 0 end
+  if y == -0 then y = 0 end
+  
+  if s then
+    return o, p, rp, math.floor(x), math.floor(y)
+  end
+end
+
+local function CreateSnapIndicator()
+  local si = CreateFrame("Frame",nil,UIParent)
+  si:SetFrameStrata("HIGH")
+  si:SetHeight(16)
+  si:SetWidth(16)
+  local tex = si:CreateTexture()
+  tex:SetAllPoints()
+  tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
+  tex:SetBlendMode("ADD")
+  tex:SetDrawLayer("OVERLAY")
+  return si
+end
+
+local si1 = CreateSnapIndicator()
+local si2 = CreateSnapIndicator()
+
+local function DisplaySnapIndicator( f, rx, ry, xOff, yOff )
+  local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff)
+  if o then
+    si1:ClearAllPoints()
+    si2:ClearAllPoints()
+    si1:SetPoint("CENTER", f, p, 0, 0)
+    local xx, yy = pointCoordFuncs[rp](o)
+    x = math.abs(x) <=rx and xx and 0 or x
+    y = math.abs(y) <=ry and yy and 0 or y
+    si2:SetPoint("CENTER", o, rp, x, y)
+    si1:Show()
+    si2:Show()
+  else
+    if si1:IsVisible() then
+      si1:Hide()
+      si2:Hide()
+    end
+  end
+  return o, p
+end
+
+local function HideSnapIndicator()
+  if si1:IsVisible() then
+    si1:Hide()
+    si2:Hide()
+  end
+end
+
+local function UpdateLabelString(bar)
+  local label = bar.controlLabelString
+  if label then
+    local name = bar.labelName
+    if name and bar.labelSubtext then
+      name = format("%s (%s)", name, bar.labelSubtext)
+    end
+    label:SetText(name or "")
+  end
+end
+
+local function CreateControls(bar)
+  local f = bar:GetFrame()
+
+  f:SetMovable(true)
+  f:SetResizable(true)
+
+  -- child of UIParent so that alpha and scale doesn't propagate to it
+  local overlay = CreateFrame("Button", nil, UIParent)
+  overlay:EnableMouse(true)
+  overlay:SetFrameLevel(3) -- set it above the buttons
+  overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4)
+  overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4)
+  overlay:SetBackdrop({
+    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
+    tile = true,
+    tileSize = 16,
+    edgeSize = 16,
+    insets = { left = 0, right = 0, top = 0, bottom = 0 },
+  })
+
+  -- textures
+  local bgTex = overlay:CreateTexture(nil,"BACKGROUND")
+  bgTex:SetTexture(0.7,0.7,1.0,0.2)
+  bgTex:SetPoint("TOPLEFT",4,-4)
+  bgTex:SetPoint("BOTTOMRIGHT",-4,4)
+  local hTex = overlay:CreateTexture(nil,"HIGHLIGHT")
+  hTex:SetTexture(0.7,0.7,1.0,0.2)
+  hTex:SetPoint("TOPLEFT",4,-4)
+  hTex:SetPoint("BOTTOMRIGHT",-4,4)
+  hTex:SetBlendMode("ADD")
+  local aTex = overlay:CreateTexture(nil,"ARTWORK")
+  aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
+  aTex:SetWidth(16)
+  aTex:SetHeight(16)
+  aTex:Hide()
+
+  -- label
+  local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
+  label:SetAllPoints()
+  label:SetJustifyH("CENTER")
+  label:SetShadowColor(0,0,0,1)
+  label:SetShadowOffset(3,-3)
+  label:SetTextColor(GetNormalTextColor())
+  label:SetText(bar:GetName())
+  label:Show()
+  bar.controlLabelString = label  -- so that bar:SetLabel() can update it
+
+  local function UpdateAnchorDecoration()
+    local point, anchor, relPoint, x, y = f:GetPoint(1)
+    if point then
+      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
+      if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then
+        bgTex:SetTexture( GetAnchoredBgColor() )
+        hTex:SetTexture( GetAnchoredBgColor() )
+        label:SetTextColor( GetAnchoredTextColor() )
+        aTex:ClearAllPoints()
+        aTex:SetPoint(point)
+        aTex:Show()
+        return
+      end
+    end
+    bgTex:SetTexture( GetNormalBgColor() )
+    hTex:SetTexture( GetNormalBgColor() )
+    label:SetTextColor( GetNormalTextColor() )
+    aTex:Hide()
+  end
+
+  local function StopResize()
+    f:StopMovingOrSizing()
+    f.isMoving = false
+    f:SetScript("OnUpdate",nil)
+    StoreSize(bar)
+    ClampToButtons(bar)
+    ReAction:RefreshOptions()
+  end
+
+  local function CornerUpdate()
+    local bw, bh = GetButtonSize(bar)
+    local r, c, s = GetButtonGrid(bar)
+    local ss = GetStateScale(bar)
+    if IsShiftKeyDown() then
+      if ss then
+        f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss )
+        f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss )
+        scale = ComputeBarScale(bar)
+      else
+        f:SetMinResize( (s+12)*c+1, (s+12)*r+1 )
+        f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 )
+        RecomputeButtonSize(bar)
+      end
+    elseif not ss and IsAltKeyDown() then
+      f:SetMinResize( bw*c, bh*r )
+      f:SetMaxResize( 2*bw*c, 2*bh*r )
+      RecomputeButtonSpacing(bar)
+    else
+      f:SetMinResize( bw+s+1, bh+s+1 )
+      f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 )
+      RecomputeGrid(bar)
+    end
+    GameTooltipTextRight2:SetText(format("%d x %d",r,c))
+
+    local ss = GetStateScale(bar)
+    local state = bar:GetState()
+    if ss then
+      GameTooltipTextRight4:SetText(format("%d%%", scale*100))
+    else
+      local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
+      GameTooltipTextRight3:SetText(size)
+      GameTooltipTextRight4:SetText(tostring(s))
+    end
+  end
+
+  -- corner drag handles
+  for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do
+    local corner = CreateFrame("Frame",nil,overlay)
+    corner:EnableMouse(true)
+    corner:SetWidth(16)
+    corner:SetHeight(16)
+    corner:SetPoint(point)
+
+    local tex = corner:CreateTexture(nil,"HIGHLIGHT")
+    tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner")
+    tex:SetTexCoord(unpack(cornerTexCoords[point]))
+    tex:SetBlendMode("ADD")
+    tex:SetAlpha(0.6)
+    tex:SetAllPoints()
+    
+    corner:SetScript("OnMouseDown",
+      function(_,btn)
+        f:SetScript("OnUpdate", CornerUpdate)
+        f:StartSizing(point)
+      end
+    )
+    corner:SetScript("OnMouseUp",
+      function()
+        local ss = GetStateScale(bar)
+        if ss then
+          local state = bar:GetState()
+          SetStateScale(bar, ComputeBarScale(bar))
+        end
+        StopResize()
+      end)
+    corner:SetScript("OnEnter",
+      function()
+        local bw, bh = GetButtonSize(bar)
+        local r, c, s = bar:GetButtonGrid()
+        local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
+        local ss = GetStateScale(bar)
+        local state = bar:GetState()
+        GameTooltip:SetOwner(f, "ANCHOR_"..point)
+        if ss then
+          GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
+        else
+          GameTooltip:AddLine(bar:GetName())
+        end
+        GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c))
+        if ss then
+          GameTooltip:AddLine(L["State Scale Override"])
+          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100))
+        else
+          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size)))
+          GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"],  L["to change spacing:"]), tostring(floor(s)))
+        end
+        GameTooltip:Show()
+      end
+    )
+    corner:SetScript("OnLeave", 
+      function()
+        GameTooltip:Hide()
+        f:SetScript("OnUpdate",nil)
+      end
+    )
+  end
+
+  overlay:RegisterForDrag("LeftButton")
+  overlay:RegisterForClicks("RightButtonUp")
+  
+  overlay:SetScript("OnDragStart",
+    function()
+      f:StartMoving()
+      f.isMoving = true
+      local w,h = bar:GetButtonSize()
+      f:ClearAllPoints()
+      UpdateAnchorDecoration()
+      f:SetScript("OnUpdate", function()
+          if IsShiftKeyDown() then
+            local f, p = DisplaySnapIndicator(f,w,h)
+          else
+            HideSnapIndicator()
+          end
+        end)
+    end
+  )
+
+  local function UpdateDragTooltip()
+    GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT")
+    local ss = GetStateScale(bar)
+    local state = bar:GetState()
+    if ss then
+      GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
+    else
+      GameTooltip:AddLine(bar:GetName())
+    end
+    GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"]))
+    GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"]))
+    GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."]))
+    local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1)
+    if point then
+      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
+      if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then
+        frame = frame or UIParent
+        GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName()))
+      end
+    end
+    GameTooltip:Show()
+  end
+
+  overlay:SetScript("OnDragStop",
+    function()
+      f:StopMovingOrSizing()
+      f.isMoving = false
+      f:SetScript("OnUpdate",nil)
+
+      if IsShiftKeyDown() then
+        local w, h = bar:GetButtonSize()
+        local a, p, rp, x, y = GetClosestPointSnapped(f,w,h)
+        if a then
+          f:ClearAllPoints()
+          f:SetPoint(p,a,rp,x,y)
+        end
+        HideSnapIndicator()
+      end
+
+      StoreExtents(bar)
+      ReAction:RefreshOptions()
+      UpdateDragTooltip()
+      UpdateAnchorDecoration()
+    end
+  )
+
+  overlay:SetScript("OnEnter",
+    function()
+      UpdateDragTooltip()
+    end
+  )
+
+  overlay:SetScript("OnLeave", HideGameTooltip)
+
+  overlay:SetScript("OnClick",
+    function()
+      ReAction:ShowEditor(bar)
+    end
+  )
+
+  function overlay:LIBKEYBOUND_ENABLED(evt)
+    self:SetFrameLevel(1)
+  end
+
+  function overlay:LIBKEYBOUND_DISABLED(evt)
+    self:SetFrameLevel(3)
+  end
+
+  function overlay:RefreshControls()
+    UpdateAnchorDecoration()
+  end
+
+  overlay:SetScript("OnShow", overlay.RefreshControls)
+
+  KB.RegisterCallback(overlay,"LIBKEYBOUND_ENABLED")
+  KB.RegisterCallback(overlay,"LIBKEYBOUND_DISABLED")
+
+  if ReAction:GetKeybindMode() then
+    overlay:SetFrameLevel(1)
+  end
+
+  bar:SetLabel(bar:GetName())
+  UpdateLabelString(bar)
+  UpdateAnchorDecoration()
+
+  return overlay
+end
+
+
+-- export methods to the Bar prototype
+
+function Bar:ShowControls(show)
+  local f = self.controlFrame
+  if show then
+    if not f then
+      f = CreateControls(self)
+      self.controlFrame = f
+    end
+    f:Show()
+  elseif f then
+    f:Hide()
+  end
+end
+
+function Bar:RefreshControls()
+  if self.controlFrame and self.controlFrame:IsShown() then
+    self.controlFrame:RefreshControls()
+  end
+end
+
+function Bar:SetLabel(name)
+  self.labelName = name
+  UpdateLabelString(self)
+end
+
+function Bar:SetLabelSubtext(text)
+  self.labelSubtext = text
+  UpdateLabelString(self)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/PetAction.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,637 @@
+--[[
+  ReAction Pet Action button module
+
+  The button module implements standard action button functionality by wrapping Blizzard's 
+  PetActionButton frame and associated functions.
+
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local CreateFrame = CreateFrame
+local format = string.format
+
+ReAction:UpdateRevision("$Revision$")
+
+-- libraries
+local KB = LibStub("LibKeyBound-1.0")
+
+-- module declaration
+local moduleID = "PetAction"
+local module = ReAction:NewModule( moduleID )
+
+-- Button class declaration
+local Button = { }
+
+-- private
+local function UpdateButtonLock(bar)
+  local f = bar:GetFrame()
+  f:SetAttribute("lockbuttons",bar.config.lockButtons)
+  f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat)
+  f:Execute(
+    [[
+      lockButtons = self:GetAttribute("lockbuttons")
+      lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
+    ]])
+end
+
+-- module methods
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID,
+    { 
+      profile = {
+        buttons = { }
+      }
+    }
+  )
+  self.buttons = { }
+
+  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
+
+  ReAction.RegisterCallback(self, "OnCreateBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  ReAction.RegisterCallback(self, "OnRenameBar")
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+
+  KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
+  KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
+end
+
+function module:OnEnable()
+  ReAction:RegisterBarType(L["Pet Action Bar"], 
+    { 
+      type = moduleID ,
+      defaultButtonSize = 30,
+      defaultBarRows = 1,
+      defaultBarCols = 10,
+      defaultBarSpacing = 8
+    })
+end
+
+function module:OnDisable()
+  ReAction:UnregisterBarType(L["Pet Action Bar"])
+end
+
+function module:OnCreateBar(event, bar, name)
+  if bar.config.type == moduleID then
+    -- auto show/hide when pet exists
+    bar:GetFrame():SetAttribute("unit","pet")
+    if not ReAction:GetConfigMode() then
+      RegisterUnitWatch(bar:GetFrame())
+    end
+    self:OnRefreshBar(event, bar, name)
+  end
+end
+
+function module:OnRefreshBar(event, bar, name)
+  if bar.config.type == moduleID then
+    if self.buttons[bar] == nil then
+      self.buttons[bar] = { }
+    end
+    local btns = self.buttons[bar]
+    local profile = self.db.profile
+    if profile.buttons[name] == nil then
+      profile.buttons[name] = {}
+    end
+    local btnCfg = profile.buttons[name]
+
+    local r, c = bar:GetButtonGrid()
+    local n = r*c
+    for i = 1, n do
+      if btnCfg[i] == nil then
+        btnCfg[i] = {}
+      end
+      if btns[i] == nil then
+        local success, r = pcall(Button.New,Button,bar,i,btnCfg[i])
+        if success and r then
+          btns[i] = r
+          bar:AddButton(i,r)
+        else
+          n = i - 1
+          bar:ClipNButtons(n)
+          break
+        end
+      end
+      btns[i]:Refresh()
+    end
+    for i = n+1, #btns do
+      if btns[i] then
+        bar:RemoveButton(btns[i])
+        btns[i] = btns[i]:Destroy()
+        if btnCfg[i] then
+          btnCfg[i] = nil
+        end
+      end
+    end
+    UpdateButtonLock(bar)
+  end
+end
+
+function module:OnDestroyBar(event, bar, name)
+  if self.buttons[bar] then
+    local btns = self.buttons[bar]
+    for _,b in pairs(btns) do
+      if b then
+        b:Destroy()
+      end
+    end
+    self.buttons[bar] = nil
+  end
+end
+
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.buttons[name] = nil
+end
+
+function module:OnRenameBar(event, bar, oldname, newname)
+  local b = self.db.profile.buttons
+  b[newname], b[oldname] = b[oldname], nil
+end
+
+
+function module:OnConfigModeChanged(event, mode)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:ShowActionIDLabel(mode)
+    end
+  end
+  for _, bar in ReAction:IterateBars() do
+    if bar and self.buttons[bar] then
+      local f = bar:GetFrame()
+      if mode then
+        UnregisterUnitWatch(f)
+        f:Show()
+      else
+        RegisterUnitWatch(f)
+      end
+    end
+  end
+end
+
+function module:LIBKEYBOUND_ENABLED(evt)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:SetKeybindMode(true)
+    end
+  end
+end
+
+function module:LIBKEYBOUND_DISABLED(evt)
+  for _, buttons in pairs(self.buttons) do
+    for _, b in pairs(buttons) do
+      b:SetKeybindMode(false)
+    end
+  end
+end
+
+
+---- Options ----
+local Handler = { }
+local meta = { __index = Handler }
+
+function Handler:New(bar)
+  return setmetatable(
+    {
+      bar = bar,
+      config = bar.config
+    }, meta)
+end
+
+function Handler:GetLockButtons()
+  return LOCK_ACTIONBAR == "1" or self.config.lockButtons
+end
+
+function Handler:SetLockButtons(info, value)
+  self.config.lockButtons = value
+  UpdateButtonLock(self.bar)
+end
+
+function Handler:LockButtonsDisabled()
+  return LOCK_ACTIONBAR == "1"
+end
+
+function Handler:GetLockButtonsCombat()
+  return self.config.lockButtonsCombat
+end
+
+function Handler:SetLockButtonsCombat(info, value)
+  self.config.lockButtonsCombat = value
+  UpdateButtonLock(self.bar)
+end
+
+function Handler:LockButtonsCombatDisabled()
+  return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
+end
+
+
+function module:GetBarOptions(bar)
+  if bar.config.type == moduleID then
+    return {
+      type = "group",
+      name = L["Pet Buttons"],
+      handler = Handler:New(bar),
+      args = {
+        lockButtons = {
+          name = L["Lock Buttons"],
+          desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
+          order = 2,
+          type = "toggle",
+          disabled = "LockButtonsDisabled",
+          get = "GetLockButtons",
+          set = "SetLockButtons",
+        },
+        lockOnlyCombat = {
+          name = L["Only in Combat"],
+          desc = L["Only lock the buttons when in combat"],
+          order = 3,
+          type = "toggle",
+          disabled = "LockButtonsCombatDisabled",
+          get = "GetLockButtonsCombat",
+          set = "SetLockButtonsCombat",
+        },
+      }
+    }
+  end
+end
+
+
+
+------ Button class ------
+
+-- use-count of action IDs
+local nActionIDs = NUM_PET_ACTION_SLOTS
+local ActionIDList = setmetatable( {}, {
+  __index = function(self, idx)
+    if idx == nil then
+      for i = 1, nActionIDs do
+        if rawget(self,i) == nil then
+          rawset(self,i,1)
+          return i
+        end
+      end
+      error("ran out of pet action IDs")
+    else
+      local c = rawget(self,idx) or 0
+      rawset(self,idx,c+1)
+      return idx
+    end
+  end,
+  __newindex = function(self,idx,value)
+    if value == nil then
+      value = rawget(self,idx)
+      if value == 1 then
+        value = nil
+      elseif value then
+        value = value - 1
+      end
+    end
+    rawset(self,idx,value)
+  end
+})
+
+local frameRecycler = {}
+local trash = CreateFrame("Frame")
+local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave
+do
+  local buttonLookup = setmetatable({},{__mode="kv"})
+
+  -- Use KeyBound-1.0 for binding, but use Override bindings instead of
+  -- regular bindings to support multiple profile use. This is a little
+  -- weird with the KeyBound dialog box (which has per-char selector as well
+  -- as an OK/Cancel box) but it's the least amount of effort to implement.
+  function GetActionName(f)
+    local b = buttonLookup[f]
+    if b then
+      return format("%s:%s", b.bar:GetName(), b.idx)
+    end
+  end
+
+  function GetHotkey(f)
+    local b = buttonLookup[f]
+    if b then
+      return KB:ToShortKey(b:GetConfig().hotkey)
+    end
+  end
+
+  function SetKey(f, key)
+    local b = buttonLookup[f]
+    if b then
+      local c = b:GetConfig()
+      if c.hotkey then
+        SetOverrideBinding(f, false, c.hotkey, nil)
+      end
+      if key then
+        SetOverrideBindingClick(f, false, key, f:GetName(), nil)
+      end
+      c.hotkey = key
+      b:DisplayHotkey(GetHotkey(f))
+    end
+  end
+
+  function FreeKey(f, key)
+    local b = buttonLookup[f]
+    if b then
+      local c = b:GetConfig()
+      if c.hotkey == key then
+        local action = f:GetActionName()
+        SetOverrideBinding(f, false, c.hotkey, nil)
+        c.hotkey = nil
+        b:DisplayHotkey(nil)
+        return action
+      end
+    end
+    return ReAction:FreeOverrideHotkey(key)
+  end
+
+  function ClearBindings(f)
+    SetKey(f, nil)
+  end
+
+  function GetBindings(f)
+    local b = buttonLookup[f]
+    if b then
+      return b:GetConfig().hotkey
+    end
+  end
+
+  function KBAttach( button )
+    local f = button:GetFrame()
+    f.GetActionName = GetActionName
+    f.GetHotkey     = GetHotkey
+    f.SetKey        = SetKey
+    f.FreeKey       = FreeKey
+    f.ClearBindings = ClearBindings
+    f.GetBindings   = GetBindings
+    buttonLookup[f] = button
+    f:SetKey(button:GetConfig().hotkey)
+    ReAction:RegisterKeybindFrame(f)
+    if ReAction:GetKeybindMode() then
+      button.border:SetVertexColor(KB:GetColorKeyBoundMode())
+      button.border:Show()
+    end
+  end
+
+  function OnEnter( self )
+    if not self.tooltipName then
+      return;
+    end
+    local uber = GetCVar("UberTooltips")
+    if self.isToken or (uber == "0") then
+      if uber == "0" then
+        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
+      else
+        GameTooltip_SetDefaultAnchor(GameTooltip, self)
+      end
+      local tooltip = self.tooltipName
+      local k = GetBindings(self)
+      if k then
+        tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE)
+      end
+      GameTooltip:SetText(tooltip)
+      if self.tooltipSubtext then
+        GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5)
+      end
+      GameTooltip:Show()
+    else
+      GameTooltip_SetDefaultAnchor(GameTooltip, self)
+      GameTooltip:SetPetAction(self:GetID())
+    end
+  end
+
+  function OnLeave()
+    GameTooltip:Hide()
+  end
+
+end
+
+local meta = { __index = Button }
+
+function Button:New( bar, idx, config )
+  -- create new self
+  self = setmetatable( 
+    { 
+      bar = bar,
+      idx = idx,
+      config = config,
+    }, meta )
+
+  local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
+  config.name = name
+  self.name = name
+  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
+  
+  -- have to recycle frames with the same name:
+  -- otherwise you either get references to old textures because named CreateFrame()
+  -- doesn't overwrite existing globals. Can't set them to nil in the global table, 
+  -- as it causes taint.
+  local parent = bar:GetFrame()
+  local f = frameRecycler[name]
+  if f then
+    f:SetParent(parent)
+  else
+    f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate")
+    -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
+    -- standard binding. We use override bindings.
+    local hotkey = _G[name.."HotKey"]
+    hotkey:SetParent(trash)
+    hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
+    hotkey:SetWidth(36)
+    hotkey:SetHeight(18)
+    hotkey:SetJustifyH("RIGHT")
+    hotkey:SetJustifyV("TOP")
+    hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
+    f.hotkey = hotkey
+    f:HookScript("OnDragStart", function() self:Update() end)
+    f:HookScript("OnReceiveDrag", function() self:Update() end)
+    f:SetScript("OnEnter", OnEnter)
+    f:SetScript("OnLeave", OnLeave)
+  end
+  if config.actionID then
+    f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton
+  end
+  f:SetFrameStrata("MEDIUM")
+  self.frame    = f
+  self.icon     = _G[("%sIcon"):format(name)]
+  self.acTex    = _G[("%sAutoCastable"):format(name)]
+  self.acModel  = _G[("%sShine"):format(name)]
+  self.cooldown = _G[("%sCooldown"):format(name)]
+  self.hotkey   = f.hotkey
+  self.border   = _G[("%sBorder"):format(name)]
+
+
+  f:RegisterEvent("PLAYER_CONTROL_LOST");
+	f:RegisterEvent("PLAYER_CONTROL_GAINED");
+	f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED");
+	f:RegisterEvent("UNIT_PET");
+	f:RegisterEvent("UNIT_FLAGS");
+	f:RegisterEvent("UNIT_AURA");
+	f:RegisterEvent("PET_BAR_UPDATE");
+	f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN");
+
+  f:SetScript("OnEvent",
+    function(event,arg1)
+      if event =="PET_BAR_UPDATE_COOLDOWN" then
+        self:UpdateCooldown()
+      elseif event == "UPDATE_BINDINGS" then
+        self:UpdateHotkey()
+      else
+        self:Update()
+      end
+    end)
+
+  -- install drag wrappers to lock buttons 
+  bar:GetFrame():WrapScript(f, "OnDragStart",
+    -- OnDragStart(self, button, kind, value, ...)
+    [[
+      if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
+        return "clear"
+      end
+    ]])
+
+  KBAttach(self)
+
+  -- attach to skinner
+  bar:SkinButton(self,
+    {
+      HotKey = self.hotkey,
+    }
+  )
+
+  self:Refresh()
+  self:SetKeybindMode(ReAction:GetKeybindMode())
+
+  return self
+end
+
+function Button:Destroy()
+  local f = self.frame
+  f:UnregisterAllEvents()
+  f:Hide()
+  f:SetParent(UIParent)
+  f:ClearAllPoints()
+  if self.name then
+    frameRecycler[self.name] = f
+    _G[self.name] = nil
+  end
+  if self.config.actionID then
+    ActionIDList[self.config.actionID] = nil
+  end
+  self.frame = nil
+  self.config = nil
+  self.bar = nil
+end
+
+function Button:Refresh()
+  self.bar:PlaceButton(self, 30, 30)
+  self:Update()
+  self:UpdateHotkey()
+  self.frame:Show()
+end
+
+function Button:GetFrame()
+  return self.frame
+end
+
+function Button:GetName()
+  return self.name
+end
+
+function Button:GetConfig()
+  return self.config
+end
+
+function Button:GetActionID()
+  return self.config.actionID
+end
+
+function Button:Update()
+  local id = self.frame:GetID()
+  local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id);
+  local f = self.frame
+  --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled)))
+
+  if isToken then
+    self.icon:SetTexture(_G[texture]);
+    f.tooltipName = _G[name];
+  else
+    self.icon:SetTexture(texture);
+    f.tooltipName = name;
+  end
+
+  f.isToken = isToken;
+	f.tooltipSubtext = subtext;
+  f:SetChecked( isActive and 1 or 0);
+
+  if autoCastAllowed then
+    self.acTex:Show();
+  else
+    self.acTex:Hide();
+  end
+
+  if autoCastEnabled then
+    AutoCastShine_AutoCastStart(self.acModel)
+  else
+    AutoCastShine_AutoCastStop(self.acModel)
+  end
+
+  if texture then
+    if GetPetActionSlotUsable(id) then
+      SetDesaturation(self.icon,nil)
+    else
+      SetDesaturation(self.icon,1)
+    end
+    self.icon:Show();
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
+  else
+    self.icon:Hide();
+    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot");
+  end
+
+  self:UpdateCooldown()
+end
+
+function Button:UpdateCooldown()
+	local start, duration, enable = GetPetActionCooldown(self.frame:GetID());
+  CooldownFrame_SetTimer(self.cooldown, start, duration, enable);
+end
+
+function Button:UpdateHotkey()
+  self:DisplayHotkey(GetHotkey(self.frame))
+end
+
+function Button:ShowActionIDLabel(show)
+  if show then
+    -- store the action ID label in the frame due to frame recycling
+    if not self.actionIDLabel and self:GetActionID() then
+      local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
+      label:SetAllPoints()
+      label:SetJustifyH("CENTER")
+      label:SetShadowColor(0,0,0,1)
+      label:SetShadowOffset(2,-2)
+      label:SetText(tostring(self:GetActionID()))
+      self.actionIDLabel = label
+    end
+    self.actionIDLabel:Show()
+  elseif self.actionIDLabel then
+    self.actionIDLabel:Hide()
+  end
+end
+
+
+function Button:SetKeybindMode(mode)
+  if mode then
+    self.border:SetVertexColor(KB:GetColorKeyBoundMode())
+    self.border:Show()
+  else
+    self.border:Hide()
+  end
+end
+
+function Button:DisplayHotkey( key )
+  self.hotkey:SetText(key or "")
+end
--- a/modules/ReAction_Action/ReAction_Action.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1157 +0,0 @@
---[[
-  ReAction Action button module.
-
-  The button module implements standard action button functionality by wrapping Blizzard's 
-  ActionBarButtonTemplate frame and associated functions.
-
-  It also provides action remapping support for multiple pages and possessed targets
-  (Mind Control, Eyes of the Beast, Karazhan Chess event, various quests, etc).
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local CreateFrame = CreateFrame
-local format = string.format
-local wipe = wipe
-
-ReAction:UpdateRevision("$Revision$")
-
--- libraries
-local KB = LibStub("LibKeyBound-1.0")
-local LBF -- initialized later
-
--- module declaration
-local moduleID = "Action"
-local module = ReAction:NewModule( moduleID )
-
--- Class declarations
-local Button = { }
-local Handle = { }
-local PropHandler = { }
-
--- Event handlers
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID,
-    { 
-      profile = {
-        bars = { },
-      }
-    }
-  )
-  self.handles = setmetatable({ }, weak)
-
-  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
-
-  ReAction.RegisterCallback(self, "OnCreateBar")
-  ReAction.RegisterCallback(self, "OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnDestroyBar")
-  ReAction.RegisterCallback(self, "OnEraseBar")
-  ReAction.RegisterCallback(self, "OnRenameBar")
-  ReAction.RegisterCallback(self, "OnConfigModeChanged")
-
-  LBF = LibStub("LibButtonFacade",true)
-
-  KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
-  KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
-  KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
-end
-
-function module:OnEnable()
-  ReAction:RegisterBarType(L["Action Bar"], 
-    { 
-      type = moduleID,
-      defaultButtonSize = 36,
-      defaultBarRows = 1,
-      defaultBarCols = 12,
-      defaultBarSpacing = 3
-    }, true)
-  ReAction:GetModule("State"):RegisterStateProperty("page", nil, PropHandler.GetOptions(), PropHandler)
-end
-
-function module:OnDisable()
-  ReAction:UnregisterBarType(L["Action Bar"])
-  ReAction:GetModule("State"):UnregisterStateProperty("page")
-end
-
-function module:OnCreateBar(event, bar, name)
-  if bar.config.type == moduleID then
-    local profile = self.db.profile
-    if profile.bars[name] == nil then
-      profile.bars[name] = {
-        buttons = { }
-      }
-    end
-    if self.handles[bar] == nil then
-      self.handles[bar] = Handle:New(bar, profile.bars[name])
-    end
-  end
-end
-
-function module:OnRefreshBar(event, bar, name)
-  if self.handles[bar] then
-    self.handles[bar]:Refresh()
-  end
-end
-
-function module:OnDestroyBar(event, bar, name)
-  if self.handles[bar] then
-    self.handles[bar]:Destroy()
-    self.handles[bar] = nil
-  end
-end
-
-function module:OnEraseBar(event, bar, name)
-  self.db.profile.bars[name] = nil
-end
-
-function module:OnRenameBar(event, bar, oldname, newname)
-  b = self.db.profile.bars
-  b[newname], b[oldname] = b[oldname], nil
-end
-
-function module:OnConfigModeChanged(event, mode)
-  for _, h in pairs(self.handles) do
-    h:SetConfigMode(mode)
-  end
-end
-
-function module:LIBKEYBOUND_ENABLED(evt)
-  for _, h in pairs(self.handles) do
-    h:ShowGrid(true)
-    h:SetKeybindMode(true)
-  end
-end
-
-function module:LIBKEYBOUND_DISABLED(evt)
-  for _, h in pairs(self.handles) do
-    h:ShowGrid(false)
-    h:SetKeybindMode(false)
-  end
-end
-
-
----- Interface ----
-function module:GetBarOptions(bar)
-  local h = self.handles[bar]
-  if h then
-    return h:GetOptions()
-  end
-end
-
-
----- Bar Handle ----
-
-do
-  local options = {
-    hideEmpty = {
-      name = L["Hide Empty Buttons"],
-      order = 1,
-      type = "toggle",
-      width = "double",
-      get  = "GetHideEmpty",
-      set  = "SetHideEmpty",
-    },
-    lockButtons = {
-      name = L["Lock Buttons"],
-      desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
-      order = 2,
-      type = "toggle",
-      disabled = "LockButtonsDisabled",
-      get = "GetLockButtons",
-      set = "SetLockButtons",
-    },
-    lockOnlyCombat = {
-      name = L["Only in Combat"],
-      desc = L["Only lock the buttons when in combat"],
-      order = 3,
-      type = "toggle",
-      disabled = "LockButtonsCombatDisabled",
-      get = "GetLockButtonsCombat",
-      set = "SetLockButtonsCombat",
-    },
-    pages = {
-      name  = L["# Pages"],
-      desc  = L["Use the Dynamic State tab to specify page transitions"],
-      order = 4,
-      type  = "range",
-      min   = 1,
-      max   = 10,
-      step  = 1,
-      get   = "GetNumPages",
-      set   = "SetNumPages",
-    },
-    mindcontrol = {
-      name = L["Mind Control Support"],
-      desc = L["When possessing a target (e.g. via Mind Control), map the first 12 buttons of this bar to the possessed target's actions."],
-      order = 5,
-      type = "toggle",
-      width = "double",
-      set = "SetMindControl",
-      get = "GetMindControl",
-    },
-    actions = {
-      name   = L["Edit Action IDs"],
-      order  = 6,
-      type   = "group",
-      inline = true,
-      args   = {
-        method = {
-          name   = L["Assign"],
-          order  = 1,
-          type   = "select",
-          width  = "double",
-          values = { [0] = L["Choose Method..."],
-                     [1] = L["Individually"],
-                     [2] = L["All at Once"], },
-          get    = "GetActionEditMethod",
-          set    = "SetActionEditMethod",
-        },
-        rowSelect = {
-          name   = L["Row"],
-          desc   = L["Rows are numbered top to bottom"],
-          order  = 2,
-          type   = "select",
-          width  = "half",
-          hidden = "IsButtonSelectHidden",
-          values = "GetRowList",
-          get    = "GetSelectedRow",
-          set    = "SetSelectedRow",
-        },
-        colSelect = {
-          name   = L["Col"],
-          desc   = L["Columns are numbered left to right"],
-          order  = 3,
-          type   = "select",
-          width  = "half",
-          hidden = "IsButtonSelectHidden",
-          values = "GetColumnList",
-          get    = "GetSelectedColumn",
-          set    = "SetSelectedColumn",
-        },
-        pageSelect = {
-          name   = L["Page"],
-          order  = 4,
-          type   = "select",
-          width  = "half",
-          hidden = "IsPageSelectHidden",
-          values = "GetPageList",
-          get    = "GetSelectedPage",
-          set    = "SetSelectedPage",
-        },
-        single = {
-          name   = L["Action ID"],
-          usage  = L["Specify ID 1-120"],
-          order  = 5,
-          type   = "input",
-          width  = "half",
-          hidden = "IsButtonSelectHidden",
-          get    = "GetActionID",
-          set    = "SetActionID",
-          validate = "ValidateActionID",
-        },
-        multi = {
-          name   = L["ID List"],
-          usage  = L["Specify a comma-separated list of IDs for each button in the bar (in order). Separate multiple pages with semicolons (;)"],
-          order  = 6,
-          type   = "input",
-          multiline = true,
-          width  = "double",
-          hidden = "IsMultiIDHidden",
-          get    = "GetMultiID",
-          set    = "SetMultiID",
-          validate = "ValidateMultiID",
-        },
-      },
-    },
-  }
-
-  local weak  = { __mode="k" }
-  local meta = { __index = Handle }
-
-  function Handle:New( bar, config )
-    local self = setmetatable(
-      {
-        bar = bar,
-        config = config,
-        btns = { }
-      }, 
-      meta)
-    
-    if self.config.buttons == nil then
-      self.config.buttons = { }
-    end
-    self:Refresh()
-    self:SetKeybindMode(ReAction:GetKeybindMode())
-    return self
-  end
-
-  function Handle:Refresh()
-    local r, c = self.bar:GetButtonGrid()
-    local n = r*c
-    local btnCfg = self.config.buttons
-    if n ~= #self.btns then
-      for i = 1, n do
-        if btnCfg[i] == nil then
-          btnCfg[i] = {}
-        end
-        if self.btns[i] == nil then
-          local b = Button:New(self, i, btnCfg[i], self.config)
-          self.btns[i] = b
-          self.bar:AddButton(i,b)
-        end
-      end
-      for i = n+1, #self.btns do
-        if self.btns[i] then
-          self.bar:RemoveButton(self.btns[i])
-          self.btns[i]:Destroy()
-          self.btns[i] = nil
-          btnCfg[i] = nil
-        end
-      end
-    end
-    local f = self.bar:GetFrame()
-    for _, b in ipairs(self.btns) do
-      b:Refresh()
-    end
-    f:SetAttribute("mindcontrol",self.config.mindcontrol)
-    f:Execute(
-      [[
-      doMindControl = self:GetAttribute("mindcontrol")
-      control:ChildUpdate()
-      ]])
-
-    f:SetAttribute("_onstate-mindcontrol",
-      -- function _onstate-mindcontrol(self, stateid, newstate)
-      [[
-        control:ChildUpdate()
-      ]])
-    RegisterStateDriver(f, "mindcontrol", "[bonusbar:5] mc; none")
-    self:UpdateButtonLock()
-  end
-
-  function Handle:Destroy()
-    for _,b in pairs(self.btns) do
-      if b then
-        b:Destroy()
-      end
-    end
-  end
-
-  function Handle:SetConfigMode(mode)
-    for _, b in pairs(self.btns) do
-      b:ShowGrid(mode)
-      b:ShowActionIDLabel(mode)
-    end
-  end
-
-  function Handle:ShowGrid(show)
-    for _, b in pairs(self.btns) do
-      b:ShowGrid(show)
-    end
-  end
-
-  function Handle:UpdateButtonLock()
-    local f = self.bar:GetFrame()
-    f:SetAttribute("lockbuttons",self.config.lockButtons)
-    f:SetAttribute("lockbuttonscombat",self.config.lockButtonsCombat)
-    f:Execute(
-      [[
-        lockButtons = self:GetAttribute("lockbuttons")
-        lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
-      ]])
-  end
-
-  function Handle:SetKeybindMode(mode)
-    for _, b in pairs(self.btns) do
-      if mode then
-        -- set the border for all buttons to the keybind-enable color
-      	b.border:SetVertexColor(KB:GetColorKeyBoundMode())
-        b.border:Show()
-      elseif IsEquippedAction(b:GetActionID()) then
-        b.border:SetVertexColor(0, 1.0, 0, 0.35) -- from ActionButton.lua
-      else
-        b.border:Hide()
-      end
-    end
-  end
-
-  function Handle:GetLastButton()
-    return self.btns[#self.btns]
-  end
-
-    -- options handlers
-  function Handle:GetOptions()
-    return {
-      type = "group",
-      name = L["Action Buttons"],
-      handler = self,
-      args = options
-    }
-  end
-
-  function Handle:SetHideEmpty(info, value)
-    if value ~= self.config.hideEmpty then
-      self.config.hideEmpty = value
-      self:ShowGrid(not value)
-    end
-  end
-
-  function Handle:GetHideEmpty()
-    return self.config.hideEmpty
-  end
-
-  function Handle:GetLockButtons()
-    return LOCK_ACTIONBAR == "1" or self.config.lockButtons
-  end
-
-  function Handle:SetLockButtons(info, value)
-    self.config.lockButtons = value
-    self:UpdateButtonLock()
-  end
-
-  function Handle:LockButtonsDisabled()
-    return LOCK_ACTIONBAR == "1"
-  end
-
-  function Handle:GetLockButtonsCombat()
-    return self.config.lockButtonsCombat
-  end
-
-  function Handle:SetLockButtonsCombat(info, value)
-    self.config.lockButtonsCombat = value
-    self:UpdateButtonLock()
-  end
-
-  function Handle:LockButtonsCombatDisabled()
-    return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
-  end
-
-  function Handle:GetNumPages()
-    return self.config.nPages
-  end
-
-  function Handle:SetNumPages(info, value)
-    self.config.nPages = value
-    self:Refresh()
-  end
-
-  function Handle:GetMindControl()
-    return self.config.mindcontrol
-  end
-
-  function Handle:SetMindControl(info, value)
-    self.config.mindcontrol = value
-    self:Refresh()
-  end
-
-  function Handle:GetActionEditMethod()
-    return self.editMethod or 0
-  end
-
-  function Handle:SetActionEditMethod(info, value)
-    self.editMethod = value
-  end
-
-  function Handle:IsButtonSelectHidden()
-    return self.editMethod ~= 1
-  end
-
-  function Handle:GetRowList()
-    local r,c = self.bar:GetButtonGrid()
-    if self.rowList == nil or #self.rowList ~= r then
-      local list = { }
-      for i = 1, r do
-        table.insert(list,i)
-      end
-      self.rowList = list
-    end
-    return self.rowList
-  end
-
-  function Handle:GetSelectedRow()
-    local r, c = self.bar:GetButtonGrid()
-    local row = self.selectedRow or 1
-    if row > r then
-      row = 1
-    end
-    self.selectedRow = row
-    return row
-  end
-
-  function Handle:SetSelectedRow(info, value)
-    self.selectedRow = value
-  end
-
-  function Handle:GetColumnList()
-    local r,c = self.bar:GetButtonGrid()
-    if self.columnList == nil or #self.columnList ~= c then
-      local list = { }
-      for i = 1, c do
-        table.insert(list,i)
-      end
-      self.columnList = list
-    end
-    return self.columnList
-  end
-
-  function Handle:GetSelectedColumn()
-    local r, c = self.bar:GetButtonGrid()
-    local col = self.selectedColumn or 1
-    if col > c then
-      col = 1
-    end
-    self.selectedColumn = col
-    return col
-  end
-
-  function Handle:SetSelectedColumn(info, value)
-    self.selectedColumn = value
-  end
-
-  function Handle:IsPageSelectHidden()
-    return self.editMethod ~= 1 or (self.config.nPages or 1) < 2
-  end
-
-  function Handle:GetPageList()
-    local n = self.config.nPages or 1
-    if self.pageList == nil or #self.pageList ~= n then
-      local p = { }
-      for i = 1, n do
-        table.insert(p,i)
-      end
-      self.pageList = p
-    end
-    return self.pageList
-  end
-
-  function Handle:GetSelectedPage()
-    local p = self.selectedPage or 1
-    if p > (self.config.nPages or 1) then
-      p = 1
-    end
-    self.selectedPage = p
-    return p
-  end
-
-  function Handle:SetSelectedPage(info, value)
-    self.selectedPage = value
-  end
-
-  function Handle:GetActionID()
-    local row = self.selectedRow or 1
-    local col = self.selectedColumn or 1
-    local r, c = self.bar:GetButtonGrid()
-    local n = (row-1) * c + col
-    local btn = self.btns[n]
-    if btn then
-      return tostring(btn:GetActionID(self.selectedPage or 1))
-    end
-  end
-
-  function Handle:SetActionID(info, value)
-    local row = self.selectedRow or 1
-    local col = self.selectedColumn or 1
-    local r, c = self.bar:GetButtonGrid()
-    local n = (row-1) * c + col
-    local btn = self.btns[n]
-    if btn then
-      btn:SetActionID(tonumber(value), self.selectedPage or 1)
-    end
-  end
-
-  function Handle:ValidateActionID(info, value)
-    value = tonumber(value)
-    if value == nil or value < 1 or value > 120 then
-      return L["Specify ID 1-120"]
-    end
-    return true
-  end
-
-  function Handle:IsMultiIDHidden()
-    return self.editMethod ~= 2
-  end
-
-  function Handle:GetMultiID()
-    local p = { }
-    for i = 1, self.config.nPages or 1 do
-      local b = { }
-      for _, btn in ipairs(self.btns) do
-        table.insert(b, btn:GetActionID(i))
-      end
-      table.insert(p, table.concat(b,","))
-    end
-    return table.concat(p,";\n")
-  end
-
-
-  local function ParseMultiID(nBtns, nPages, s)
-    if s:match("[^%d%s,;]") then
-      ReAction:Print("items other than digits, spaces, commas, and semicolons in string",s)
-      return nil
-    end
-    local p = { }
-    for list in s:gmatch("[^;]+") do
-      local pattern = ("^%s?$"):format(("%s*(%d+)%s*,"):rep(nBtns))
-      local ids = { list:match(pattern) }
-      if #ids ~= nBtns then
-        ReAction:Print("found",#ids,"buttons instead of",nBtns)
-        return nil
-      end
-      table.insert(p,ids)
-    end
-    if #p ~= nPages then
-      ReAction:Print("found",#p,"pages instead of",nPages)
-      return nil
-    end
-    return p
-  end
-
-  function Handle:SetMultiID(info, value)
-    local p = ParseMultiID(#self.btns, self.config.nPages or 1, value)
-    for page, b in ipairs(p) do
-      for button, id in ipairs(b) do
-        self.btns[button]:SetActionID(id, page)
-      end
-    end
-  end
-
-  function Handle:ValidateMultiID(info, value)
-    local bad = L["Invalid action ID list string"]
-    if value == nil or ParseMultiID(#self.btns, self.config.nPages or 1, value) == nil then
-      return bad
-    end
-    return true
-  end
-end
-
-
------- State property options ------
-do
-  local pageOptions = {
-    page = {
-      name     = L["Show Page #"],
-      order    = 11,
-      type     = "select",
-      width    = "half",
-      disabled = "IsPageDisabled",
-      hidden   = "IsPageHidden",
-      values   = "GetPageValues",
-      set      = "SetProp",
-      get      = "GetPage",
-    },
-  }
-
-  local function GetBarConfig(bar)
-    return module.db.profile.bars[bar:GetName()]
-  end
-
-  function PropHandler.GetOptions()
-    return pageOptions
-  end
-
-  function PropHandler:IsPageDisabled()
-    local c = GetBarConfig(self.bar)
-    local n = c and c.nPages or 1
-    return not (n > 1)
-  end
-
-  function PropHandler:IsPageHidden()
-    return not GetBarConfig(self.bar)
-  end
-
-  function PropHandler:GetPageValues()
-    if not self._pagevalues then
-      self._pagevalues = { }
-    end
-    local c = GetBarConfig(self.bar)
-    if c then
-      local n = c.nPages
-        -- cache the results
-      if self._npages ~= n then
-        self._npages = n
-        wipe(self._pagevalues)
-        for i = 1, n do
-          self._pagevalues["page"..i] = i
-        end
-      end
-    end
-    return self._pagevalues
-  end
-
-  function PropHandler:GetPage(info)
-    return self:GetProp(info) or 1
-  end
-
-end
-
------- ActionID allocation ------
--- this needs to be high performance when requesting new IDs,
--- or certain controls will become sluggish. However, the new-request
--- infrastructure can be built lazily the first time that a new request
--- comes in (which will only happen at user config time: at static startup
--- config time all actionIDs should already have been assigned and stored
--- in the config file)
-
-local IDAlloc
-do
-  local n = 120
-  
-  IDAlloc = setmetatable({ wrap = 1, freecount = n }, {__index = function() return 0 end})
-
-  function IDAlloc:Acquire(id, hint)
-    id = tonumber(id)
-    hint = tonumber(hint)
-    if id and (id < 1 or id > n) then
-      id = nil
-    end
-    if hint and (hint < 1 or hint > n) then
-      hint = nil
-    end
-    if id == nil then
-      -- get a free ID
-      if hint and self[hint] == 0 then
-        -- use the hint if it's free
-        id = hint
-      elseif self.freecount > 0 then
-        -- if neither the id nor the hint are defined or free, but
-        -- the list is known to have free IDs, then start searching
-        -- at the hint for a free one
-        for i = hint or 1, n do
-          if self[i] == 0 then
-            id = i
-            break
-          end
-        end
-        -- self.wrap the search
-        if id == nil and hint and hint > 1 then
-          for i = 1, hint - 1 do
-            if self[i] == 0 then
-              id = i
-              break
-            end
-          end
-        end
-      end
-      if id == nil then
-        -- if there are no free IDs, start wrapping at 1
-        id = self.wrap
-        self.wrap = id + 1
-        if self.wrap > n then
-          self.wrap = 1
-        end
-      end
-    end
-    if self[id] == 0 then
-      self.freecount = self.freecount - 1
-    end
-    self[id] = self[id] + 1
-    return id
-  end
-
-  function IDAlloc:Release(id)
-    id = tonumber(id)
-    if id and (id >= 1 or id <= n) then
-      self[id] = self[id] - 1
-      if self[id] == 0 then
-        self.freecount = self.freecount + 1
-        self.wrap = 1
-      end
-    end
-  end
-end
-
------- Button class ------
-
-do
-  local frameRecycler = { }
-  local trash = CreateFrame("Frame")
-  local OnUpdate, KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings
-  do
-    local ATTACK_BUTTON_FLASH_TIME = ATTACK_BUTTON_FLASH_TIME
-    local IsActionInRange = IsActionInRange
-
-    local buttonLookup = setmetatable({},{__mode="kv"})
-
-    function OnUpdate(frame, elapsed)
-      -- note: This function taints frame.flashtime and frame.rangeTimer. Both of these
-      --       are only read by ActionButton_OnUpdate (which this function replaces). In
-      --       all other places they're just written, so it doesn't taint any secure code.
-      if frame.flashing == 1 then
-        frame.flashtime = frame.flashtime - elapsed
-        if frame.flashtime <= 0 then
-          local overtime = -frame.flashtime
-          if overtime >= ATTACK_BUTTON_FLASH_TIME then
-            overtime = 0
-          end
-          frame.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
-
-          local flashTexture = frame.flash
-          if flashTexture:IsShown() then
-            flashTexture:Hide()
-          else
-            flashTexture:Show()
-          end
-        end
-      end
-      
-      if frame.rangeTimer then
-        frame.rangeTimer = frame.rangeTimer - elapsed;
-
-        if frame.rangeTimer <= 0 then
-          if IsActionInRange(frame.action) == 0 then
-            frame.icon:SetVertexColor(1.0,0.1,0.1)
-          else
-            ActionButton_UpdateUsable(frame)
-          end
-          frame.rangeTimer = 0.1
-        end
-      end
-    end
-
-    -- Use KeyBound-1.0 for binding, but use Override bindings instead of
-    -- regular bindings to support multiple profile use. This is a little
-    -- weird with the KeyBound dialog box (which has per-char selector as well
-    -- as an OK/Cancel box) but it's the least amount of effort to implement.
-    function GetActionName(f)
-      local b = buttonLookup[f]
-      if b then
-        return format("%s:%s", b.bar:GetName(), b.idx)
-      end
-    end
-
-    function GetHotkey(f)
-      local b = buttonLookup[f]
-      if b then
-        return KB:ToShortKey(b:GetConfig().hotkey)
-      end
-    end
-
-    function SetKey(f, key)
-      local b = buttonLookup[f]
-      if b then
-        local c = b:GetConfig()
-        if c.hotkey then
-          SetOverrideBinding(f, false, c.hotkey, nil)
-        end
-        if key then
-          SetOverrideBindingClick(f, false, key, f:GetName(), nil)
-        end
-        c.hotkey = key
-        b:DisplayHotkey(GetHotkey(f))
-      end
-    end
-
-    function FreeKey(f, key)
-      local b = buttonLookup[f]
-      if b then
-        local c = b:GetConfig()
-        if c.hotkey == key then
-          local action = f:GetActionName()
-          SetOverrideBinding(f, false, c.hotkey, nil)
-          c.hotkey = nil
-          b:DisplayHotkey(nil)
-          return action
-        end
-      end
-      return ReAction:FreeOverrideHotkey(key)
-    end
-
-    function ClearBindings(f)
-      SetKey(f, nil)
-    end
-
-    function GetBindings(f)
-      local b = buttonLookup[f]
-      if b then
-        return b:GetConfig().hotkey
-      end
-    end
-
-    function KBAttach( button )
-      local f = button:GetFrame()
-      f.GetActionName = GetActionName
-      f.GetHotkey     = GetHotkey
-      f.SetKey        = SetKey
-      f.FreeKey       = FreeKey
-      f.ClearBindings = ClearBindings
-      f.GetBindings   = GetBindings
-      buttonLookup[f] = button
-      f:SetKey(button:GetConfig().hotkey)
-      ReAction:RegisterKeybindFrame(f)
-      if ReAction:GetKeybindMode() then
-      	button.border:SetVertexColor(KB:GetColorKeyBoundMode())
-        button.border:Show()
-      end
-    end
-  end
-
-  local meta = {__index = Button}
-
-  function Button:New( handle, idx, config, barConfig )
-    local bar = handle.bar
-
-    -- create new self
-    self = setmetatable( 
-      { 
-        bar = bar,
-        idx = idx,
-        config = config,
-        barConfig = barConfig,
-      }, meta )
-
-    local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
-    self.name = name
-    config.name = name
-    local lastButton = handle:GetLastButton()
-    config.actionID = IDAlloc:Acquire(config.actionID, lastButton and lastButton.config.actionID) -- gets a free one if none configured
-    self.nPages = 1
-    
-    -- have to recycle frames with the same name: CreateFrame() doesn't overwrite
-    -- existing globals. Can't set to nil in the global because it's then tainted.
-    local parent = bar:GetFrame()
-    local f = frameRecycler[name]
-    if f then
-      f:SetParent(parent)
-    else
-      f = CreateFrame("CheckButton", name, parent, "ActionBarButtonTemplate")
-      -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
-      -- standard binding. We use override bindings.
-      local hotkey = _G[name.."HotKey"]
-      hotkey:SetParent(trash)
-      hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
-      hotkey:SetWidth(36)
-      hotkey:SetHeight(18)
-      hotkey:SetJustifyH("RIGHT")
-      hotkey:SetJustifyV("TOP")
-      hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
-      f.hotkey = hotkey
-      f.icon = _G[name.."Icon"]
-      f.flash = _G[name.."Flash"]
-      f:SetScript("OnUpdate",OnUpdate)
-    end
-
-    self.hotkey = f.hotkey
-    self.border = _G[name.."Border"]
-
-    f:SetAttribute("action", config.actionID)
-    -- install mind control actions for all buttons just for simplicity
-    if self.idx <= 12 then
-      f:SetAttribute("*action-mc", 120 + self.idx)
-    end
-    
-    -- set a _childupdate handler, called within the header's context
-    f:SetAttribute("_childupdate", 
-      -- function _childupdate(self, snippetid, message)
-      [[
-        local action = "action"
-        if doMindControl and GetBonusBarOffset() == 5 then
-          action = "*action-mc"
-        elseif page and state and page[state] then
-          action = "*action-"..page[state]
-        end
-        local value = self:GetAttribute(action)
-        self:SetAttribute("action",value)
-      ]])
-
-    -- install drag wrappers to lock buttons 
-    bar:GetFrame():WrapScript(f, "OnDragStart",
-      -- OnDragStart(self, button, kind, value, ...)
-      [[
-        if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
-          return "clear"
-        end
-      ]])
-
-    self.frame = f
-
-
-    -- initialize the hide state
-    f:SetAttribute("showgrid",0)
-    self:ShowGrid(not barConfig.hideEmpty)
-    if ReAction:GetConfigMode() then
-      self:ShowGrid(true)
-    end
-
-    -- show the ID label if applicable
-    self:ShowActionIDLabel(ReAction:GetConfigMode())
-
-    -- attach the keybinder
-    KBAttach(self)
-
-    -- attach to skinner
-    bar:SkinButton(self,
-      {
-        HotKey = self.hotkey,
-      }
-    )
-
-    self:Refresh()
-    return self
-  end
-
-  function Button:Destroy()
-    local f = self.frame
-    f:UnregisterAllEvents()
-    f:Hide()
-    f:SetParent(UIParent)
-    f:ClearAllPoints()
-    if self.name then
-      frameRecycler[self.name] = f
-    end
-    if self.config.actionID then
-      IDAlloc:Release(self.config.actionID)
-    end
-    if self.config.pageactions then
-      for _, id in ipairs(self.config.pageactions) do
-        IDAlloc:Release(id)
-      end
-    end
-    self.frame = nil
-    self.config = nil
-    self.bar = nil
-  end
-
-  function Button:Refresh()
-    local f = self.frame
-    self.bar:PlaceButton(self, 36, 36)
-    self:RefreshPages()
-  end
-
-  function Button:GetFrame()
-    return self.frame
-  end
-
-  function Button:GetName()
-    return self.name
-  end
-
-  function Button:GetConfig()
-    return self.config
-  end
-
-  function Button:GetActionID(page)
-    if page == nil then
-      -- get the effective ID
-      return self.frame.action -- kept up-to-date by Blizzard's ActionButton_CalculateAction()
-    else
-      if page == 1 then
-        return self.config.actionID
-      else
-        return self.config.pageactions and self.config.pageactions[page] or self.config.actionID
-      end
-    end
-  end
-
-  function Button:SetActionID( id, page )
-    id = tonumber(id)
-    page = tonumber(page)
-    if id == nil or id < 1 or id > 120 then
-      error("Button:SetActionID - invalid action ID")
-    end
-    if page and page ~= 1 then
-      if not self.config.pageactions then
-        self.config.pageactions = { }
-      end
-      if self.config.pageactions[page] then
-        IDAlloc:Release(self.config.pageactions[page])
-      end
-      self.config.pageactions[page] = id
-      IDAlloc:Acquire(self.config.pageactions[page])
-      self.frame:SetAttribute(("*action-page%d"):format(page),id)
-    else
-      IDAlloc:Release(self.config.actionID)
-      self.config.actionID = id
-      IDAlloc:Acquire(self.config.actionID)
-      self.frame:SetAttribute("action",id)
-      if self.config.pageactions then
-        self.config.pageactions[1] = id
-        self.frame:SetAttribute("*action-page1",id)
-      end
-    end
-  end
-
-  function Button:RefreshPages( force )
-    local nPages = self.barConfig.nPages
-    if nPages and (nPages ~= self.nPages or force) then
-      local f = self:GetFrame()
-      local c = self.config.pageactions
-      if nPages > 1 and not c then
-        c = { }
-        self.config.pageactions = c
-      end
-      for i = 1, nPages do
-        if i > 1 then
-          c[i] = IDAlloc:Acquire(c[i], self.config.actionID + (i-1)*self.bar:GetNumButtons())
-        else
-          c[i] = self.config.actionID  -- page 1 is the same as the base actionID
-        end
-        f:SetAttribute(("*action-page%d"):format(i),c[i])
-      end
-      for i = nPages+1, #c do
-        IDAlloc:Release(c[i])
-        c[i] = nil
-        f:SetAttribute(("*action-page%d"):format(i),nil)
-      end
-      self.nPages = nPages
-    end
-  end
-
-  function Button:ShowGrid( show )
-    if not InCombatLockdown() then
-      local f = self.frame
-      local count = f:GetAttribute("showgrid")
-      if show then
-        count = count + 1
-      else
-        count = count - 1
-      end
-      if count < 0 then
-        count = 0
-      end
-      f:SetAttribute("showgrid",count)
-
-      if count >= 1 and not f:GetAttribute("statehidden") then
-        if LBF then
-          LBF:SetNormalVertexColor(self.frame, 1.0, 1.0, 1.0, 0.5)
-        else
-          self.frame:GetNormalTexture():SetVertexColor(1.0, 1.0, 1.0, 0.5);
-        end
-        f:Show()
-      elseif count < 1 and not HasAction(self:GetActionID()) then
-        f:Hide()
-      end
-    end
-  end
-
-  function Button:ShowActionIDLabel( show )
-    local f = self:GetFrame()
-    if show then
-      local id = self:GetActionID()
-      if not f.actionIDLabel then
-        local label = f:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
-        label:SetAllPoints()
-        label:SetJustifyH("CENTER")
-        label:SetShadowColor(0,0,0,1)
-        label:SetShadowOffset(2,-2)
-        f.actionIDLabel = label -- store the label with the frame for recycling
-
-        f:HookScript("OnAttributeChanged", 
-          function(frame, attr, value)
-            if label:IsVisible() and attr:match("action") then
-              label:SetText(tostring(frame.action))
-            end
-          end)
-      end
-      f.actionIDLabel:SetText(tostring(id))
-      f.actionIDLabel:Show()
-    elseif f.actionIDLabel then
-      f.actionIDLabel:Hide()
-    end
-  end
-
-  function Button:DisplayHotkey( key )
-    self.hotkey:SetText(key or "")
-  end
-end
--- a/modules/ReAction_Action/ReAction_Action.toc	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 30000
-## Title: ReAction: Action
-## Notes: Action button module for ReAction
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_Action.xml
--- a/modules/ReAction_Action/ReAction_Action.xml	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" 
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
-
-  <Script file="ReAction_Action.lua"/>
-
-</Ui>
\ No newline at end of file
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,115 +0,0 @@
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-
--- module declaration
-local moduleID = "ButtonFacade"
-local module = ReAction:NewModule( moduleID )
-
--- handlers
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID,
-    {
-      profile = {
-        -- default profile goes here
-      }
-    }
-  )
-
-  local LBF = LibStub("LibButtonFacade",true)
-
-  if not LBF then -- no more initialization
-    return
-  end
-
-  self.LBF = LBF
-  self.groups = { }
-
-  -- override a method of ReAction.Bar
-  -- note that 'self' in this context refers to the bar
-  function ReAction.Bar:SkinButton( button, data )
-    module:GetGroup(self:GetName()):AddButton(button:GetFrame(), data)
-  end
-
-  -- register some common events
-  ReAction.RegisterCallback(self, "OnCreateBar")
-  ReAction.RegisterCallback(self, "OnDestroyBar")
-  ReAction.RegisterCallback(self, "OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnEraseBar")
-  ReAction.RegisterCallback(self, "OnRenameBar")
-
-  self.LBF:RegisterSkinCallback("ReAction", self.OnSkinChanged, self)
-end
-
-function module:OnEnable()
-
-end
-
-function module:OnDisable()
-
-end
-
-function module:OnCreateBar(event, bar, name)
-  local c = self.db.profile[name]
-  if not c then
-     c = { 
-      skinID = "Blizzard",
-      backdrop = true,
-      gloss = 0,
-      colors = {},
-    }
-    self.db.profile[name] = c
-  end
-
-  local g = self:GetGroup(name)
-  g.SkinID   = c.skinID or "Blizzard"
-  g.Backdrop = c.backdrop
-  g.Gloss    = c.gloss
-  g.Colors   = c.colors
-end
-
-function module:OnDestroyBar(event, bar, name)
-  if self.groups[name] then
-    self.groups[name]:Delete()
-    self.groups[name] = nil
-  end
-end
-
-function module:OnRefreshBar(event, bar, name)
-  local c = self.db.profile[name]
-  local g = self.groups[name]
-  if c and g then
-    g:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
-  end
-end
-
-function module:OnEraseBar(event, bar, name)
-  self:OnDestroyBar(event, bar, name)
-  self.db.profile[name] = nil
-end
-
-function module:OnRenameBar(event, bar, oldName, newName)
-  if self.groups[name] then
-    self.groups[name]:Delete(true)
-    self.db.profile[oldName], self.db.profile[newName] = nil, self.db.profile[oldName]
-    self:OnCreateBar(event, bar, newName)
-  end
-end
-
-function module:OnSkinChanged( skinID, gloss, backdrop, group, button, colors )
-  local c = self.db.profile[group]
-  if c then
-    c.skinID   = skinID
-    c.gloss    = gloss
-    c.backdrop = backdrop
-    c.colors   = colors
-  end  
-end
-
-function module:GetGroup( name )
-  if not self.groups[name] then
-    self.groups[name] = self.LBF:Group("ReAction", name)
-  end
-  return self.groups[name]
-end
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.toc	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 30000
-## Title: ReAction: ButtonFacade
-## Notes: ButtonFacade support for ReAction
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_ButtonFacade.xml
--- a/modules/ReAction_ButtonFacade/ReAction_ButtonFacade.xml	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" 
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
-
-<Script file="ReAction_ButtonFacade.lua"/>
-
-</Ui>
\ No newline at end of file
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +0,0 @@
---[[
-  ReAction 'Hide Blizzard' module
-
-  Hides Blizzard action bars. This hides the extra action bars, stance bar, pet bar, and 
-  main menu bar, which in turn hides the experience bar, bag bar, micro menu bar, and lag meter.
-
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-
-ReAction:UpdateRevision("$Revision$")
-
--- module declaration
-local moduleID = "HideBlizzard"
-local module = ReAction:NewModule( moduleID )
-
-
--- module methods
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID,
-    { 
-      profile = {
-        hide = false
-      }
-    }
-  )
-  self.db.RegisterCallback(self,"OnProfileChanged")
-  self.db.RegisterCallback(self,"OnProfileCopied", "OnProfileChanged")
-  self.db.RegisterCallback(self,"OnProfileReset",  "OnProfileChanged")
-
-  self.hiddenFrame = CreateFrame("Frame")
-  self.hiddenFrame:Hide()
-
-  ReAction:RegisterOptions(self, {
-      hideBlizzard = {
-        type = "toggle",
-        handler = self,
-        name = L["Hide Blizzard Action Bars"],
-        desc = L["Hide the default main bar and extra action bars"],
-        get  = "IsHidden",
-        set  = function(info,value) self:SetHidden(value) end,
-        disabled = InCombatLockdown
-      },
-    }, true) -- global
-
-end
-
-function module:OnEnable()
-  if self.db.profile.hide then
-    self:HideAll(true)
-  end
-
-  -- reroute blizzard action bar config to ReAction config window
-  InterfaceOptionsActionBarsPanel:HookScript("OnShow", 
-    function() 
-      if module:IsEnabled() and module:IsHidden() then
-        ReAction:ShowConfig()
-      end
-    end )
-end
-
-function module:OnDisable()
-  self:ShowAll(true)
-end
-
-function module:OnProfileChanged()
-  if self.db.profile.hide then
-    module:HideAll(true)
-  else
-    module:ShowAll(true)
-  end
-end
-
-local frames = {
-  MainMenuBar,
-  MultiBarLeft,
-  MultiBarRight,
-  MultiBarBottomLeft,
-  MultiBarBottomRight,
-  -- possess bar frame needs to be pulled out separately: stash its children away
-  PossessBarLeft,
-  PossessBarRight,
-  PossessButton1,
-  PossessButton2
-}
-
-local hidden = { }
-
-function module:HideAll( force )
-  if not(self.db.profile.hide) or force then
-    self.db.profile.hide = true
-    for _, f in pairs(frames) do
-      hidden[f] = hidden[f] or { parent = f:GetParent(), wasShown = f:IsShown() }
-      f:SetParent(self.hiddenFrame)
-      f:Hide()
-    end
-  end
-  PossessBarFrame:SetParent(UIParent)
-  PossessBarFrame:EnableMouse(false)
-end
-
-function module:ShowAll( force )
-  PossessBarFrame:EnableMouse(true)
-  PossessBarFrame:SetParent(MainMenuBar)
-  if self.db.profile.hide or force then
-    self.db.profile.hide = false
-
-    for _, f in pairs(frames) do
-      local h = hidden[f]
-      if h then
-        f:SetParent(h.parent)
-        if h.wasShown then
-          f:Show()
-        end
-      end
-    end
-  end
-end
-
-function module:IsHidden()
-  return self.db.profile.hide
-end
-
-function module:SetHidden(h)
-  if h then
-    self:HideAll()
-  else
-    self:ShowAll()
-  end
-end
-
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.toc	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 30000
-## Title: ReAction: Hide Blizzard Frames
-## Notes: Hides blizzard action bars
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_HideBlizzard.xml
--- a/modules/ReAction_HideBlizzard/ReAction_HideBlizzard.xml	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" 
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
-
-<Script file="ReAction_HideBlizzard.lua"/>
-
-</Ui>
\ No newline at end of file
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
---[[
-  ReAction module template
-
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-
--- module declaration
-local moduleID = "MyModuleName"
-local module = ReAction:NewModule( moduleID,
-  -- mixins go here
-)
-
--- handlers
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID
-    {
-      profile = {
-        -- default profile goes here
-      }
-    }
-  )
-
-  -- register some common events
-  ReAction.RegisterCallback(self, "OnCreateBar")
-  ReAction.RegisterCallback(self, "OnDestroyBar")
-  ReAction.RegisterCallback(self, "OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnEraseBar")
-  ReAction.RegisterCallback(self, "OnRenameBar")
-  ReAction.RegisterCallback(self, "OnConfigModeChanged")
-end
-
-function module:OnEnable()
-
-end
-
-function module:OnDisable()
-
-end
-
--- apply module features and settings to a bar object (see Bar.lua for Bar API)
-function module:OnCreateBar(event, bar, name)
-
-end
-
--- remove module features and settings from a bar object
-function module:OnDestroyBar(event, bar, name)
-
-end
-
--- refresh module features and settings on a bar object
-function module:OnRefreshBar(event, bar, name)
-
-end
-
--- erase any local configuration entries for the supplied bar name
-function module:OnEraseBar(event, bar, name)
-
-end
-
--- update any local configuration/option entries with the new bar name index
-function module:OnRenameBar(event, bar, oldName, newName)
-
-end
-
--- update any local display/options based on config mode (true/false)
-function module:OnConfigModeChanged(event, mode)
-
-end
-
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.toc	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 30000
-## Title: ReAction: <ModuleName>
-## Notes: <description here>
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_<ModuleName>.xml
--- a/modules/ReAction_ModuleTemplate/ReAction_ModuleName.xml	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" 
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
-
-<Script file="ReAction_ModuleName.lua"/>
-
-</Ui>
\ No newline at end of file
--- a/modules/ReAction_PetAction/ReAction_PetAction.lua	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,637 +0,0 @@
---[[
-  ReAction Pet Action button module
-
-  The button module implements standard action button functionality by wrapping Blizzard's 
-  PetActionButton frame and associated functions.
-
---]]
-
--- local imports
-local ReAction = ReAction
-local L = ReAction.L
-local _G = _G
-local CreateFrame = CreateFrame
-local format = string.format
-
-ReAction:UpdateRevision("$Revision$")
-
--- libraries
-local KB = LibStub("LibKeyBound-1.0")
-
--- module declaration
-local moduleID = "PetAction"
-local module = ReAction:NewModule( moduleID )
-
--- Button class declaration
-local Button = { }
-
--- private
-local function UpdateButtonLock(bar)
-  local f = bar:GetFrame()
-  f:SetAttribute("lockbuttons",bar.config.lockButtons)
-  f:SetAttribute("lockbuttonscombat",bar.config.lockButtonsCombat)
-  f:Execute(
-    [[
-      lockButtons = self:GetAttribute("lockbuttons")
-      lockButtonsCombat = self:GetAttribute("lockbuttonscombat")
-    ]])
-end
-
--- module methods
-function module:OnInitialize()
-  self.db = ReAction.db:RegisterNamespace( moduleID,
-    { 
-      profile = {
-        buttons = { }
-      }
-    }
-  )
-  self.buttons = { }
-
-  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
-
-  ReAction.RegisterCallback(self, "OnCreateBar")
-  ReAction.RegisterCallback(self, "OnDestroyBar")
-  ReAction.RegisterCallback(self, "OnRefreshBar")
-  ReAction.RegisterCallback(self, "OnEraseBar")
-  ReAction.RegisterCallback(self, "OnRenameBar")
-  ReAction.RegisterCallback(self, "OnConfigModeChanged")
-
-  KB.RegisterCallback(self, "LIBKEYBOUND_ENABLED")
-  KB.RegisterCallback(self, "LIBKEYBOUND_DISABLED")
-  KB.RegisterCallback(self, "LIBKEYBOUND_MODE_COLOR_CHANGED","LIBKEYBOUND_ENABLED")
-end
-
-function module:OnEnable()
-  ReAction:RegisterBarType(L["Pet Action Bar"], 
-    { 
-      type = moduleID ,
-      defaultButtonSize = 30,
-      defaultBarRows = 1,
-      defaultBarCols = 10,
-      defaultBarSpacing = 8
-    })
-end
-
-function module:OnDisable()
-  ReAction:UnregisterBarType(L["Pet Action Bar"])
-end
-
-function module:OnCreateBar(event, bar, name)
-  if bar.config.type == moduleID then
-    -- auto show/hide when pet exists
-    bar:GetFrame():SetAttribute("unit","pet")
-    if not ReAction:GetConfigMode() then
-      RegisterUnitWatch(bar:GetFrame())
-    end
-    self:OnRefreshBar(event, bar, name)
-  end
-end
-
-function module:OnRefreshBar(event, bar, name)
-  if bar.config.type == moduleID then
-    if self.buttons[bar] == nil then
-      self.buttons[bar] = { }
-    end
-    local btns = self.buttons[bar]
-    local profile = self.db.profile
-    if profile.buttons[name] == nil then
-      profile.buttons[name] = {}
-    end
-    local btnCfg = profile.buttons[name]
-
-    local r, c = bar:GetButtonGrid()
-    local n = r*c
-    for i = 1, n do
-      if btnCfg[i] == nil then
-        btnCfg[i] = {}
-      end
-      if btns[i] == nil then
-        local success, r = pcall(Button.New,Button,bar,i,btnCfg[i])
-        if success and r then
-          btns[i] = r
-          bar:AddButton(i,r)
-        else
-          n = i - 1
-          bar:ClipNButtons(n)
-          break
-        end
-      end
-      btns[i]:Refresh()
-    end
-    for i = n+1, #btns do
-      if btns[i] then
-        bar:RemoveButton(btns[i])
-        btns[i] = btns[i]:Destroy()
-        if btnCfg[i] then
-          btnCfg[i] = nil
-        end
-      end
-    end
-    UpdateButtonLock(bar)
-  end
-end
-
-function module:OnDestroyBar(event, bar, name)
-  if self.buttons[bar] then
-    local btns = self.buttons[bar]
-    for _,b in pairs(btns) do
-      if b then
-        b:Destroy()
-      end
-    end
-    self.buttons[bar] = nil
-  end
-end
-
-function module:OnEraseBar(event, bar, name)
-  self.db.profile.buttons[name] = nil
-end
-
-function module:OnRenameBar(event, bar, oldname, newname)
-  local b = self.db.profile.buttons
-  b[newname], b[oldname] = b[oldname], nil
-end
-
-
-function module:OnConfigModeChanged(event, mode)
-  for _, buttons in pairs(self.buttons) do
-    for _, b in pairs(buttons) do
-      b:ShowActionIDLabel(mode)
-    end
-  end
-  for _, bar in ReAction:IterateBars() do
-    if bar and self.buttons[bar] then
-      local f = bar:GetFrame()
-      if mode then
-        UnregisterUnitWatch(f)
-        f:Show()
-      else
-        RegisterUnitWatch(f)
-      end
-    end
-  end
-end
-
-function module:LIBKEYBOUND_ENABLED(evt)
-  for _, buttons in pairs(self.buttons) do
-    for _, b in pairs(buttons) do
-      b:SetKeybindMode(true)
-    end
-  end
-end
-
-function module:LIBKEYBOUND_DISABLED(evt)
-  for _, buttons in pairs(self.buttons) do
-    for _, b in pairs(buttons) do
-      b:SetKeybindMode(false)
-    end
-  end
-end
-
-
----- Options ----
-local Handler = { }
-local meta = { __index = Handler }
-
-function Handler:New(bar)
-  return setmetatable(
-    {
-      bar = bar,
-      config = bar.config
-    }, meta)
-end
-
-function Handler:GetLockButtons()
-  return LOCK_ACTIONBAR == "1" or self.config.lockButtons
-end
-
-function Handler:SetLockButtons(info, value)
-  self.config.lockButtons = value
-  UpdateButtonLock(self.bar)
-end
-
-function Handler:LockButtonsDisabled()
-  return LOCK_ACTIONBAR == "1"
-end
-
-function Handler:GetLockButtonsCombat()
-  return self.config.lockButtonsCombat
-end
-
-function Handler:SetLockButtonsCombat(info, value)
-  self.config.lockButtonsCombat = value
-  UpdateButtonLock(self.bar)
-end
-
-function Handler:LockButtonsCombatDisabled()
-  return LOCK_ACTIONBAR == "1" or not self.config.lockButtons
-end
-
-
-function module:GetBarOptions(bar)
-  if bar.config.type == moduleID then
-    return {
-      type = "group",
-      name = L["Pet Buttons"],
-      handler = Handler:New(bar),
-      args = {
-        lockButtons = {
-          name = L["Lock Buttons"],
-          desc = L["Prevents picking up/dragging actions.|nNOTE: This setting is overridden by the global setting in Blizzard's Action Buttons tab"],
-          order = 2,
-          type = "toggle",
-          disabled = "LockButtonsDisabled",
-          get = "GetLockButtons",
-          set = "SetLockButtons",
-        },
-        lockOnlyCombat = {
-          name = L["Only in Combat"],
-          desc = L["Only lock the buttons when in combat"],
-          order = 3,
-          type = "toggle",
-          disabled = "LockButtonsCombatDisabled",
-          get = "GetLockButtonsCombat",
-          set = "SetLockButtonsCombat",
-        },
-      }
-    }
-  end
-end
-
-
-
------- Button class ------
-
--- use-count of action IDs
-local nActionIDs = NUM_PET_ACTION_SLOTS
-local ActionIDList = setmetatable( {}, {
-  __index = function(self, idx)
-    if idx == nil then
-      for i = 1, nActionIDs do
-        if rawget(self,i) == nil then
-          rawset(self,i,1)
-          return i
-        end
-      end
-      error("ran out of pet action IDs")
-    else
-      local c = rawget(self,idx) or 0
-      rawset(self,idx,c+1)
-      return idx
-    end
-  end,
-  __newindex = function(self,idx,value)
-    if value == nil then
-      value = rawget(self,idx)
-      if value == 1 then
-        value = nil
-      elseif value then
-        value = value - 1
-      end
-    end
-    rawset(self,idx,value)
-  end
-})
-
-local frameRecycler = {}
-local trash = CreateFrame("Frame")
-local KBAttach, GetActionName, GetHotkey, SetKey, FreeKey, ClearBindings, GetBindings, OnEnter, OnLeave
-do
-  local buttonLookup = setmetatable({},{__mode="kv"})
-
-  -- Use KeyBound-1.0 for binding, but use Override bindings instead of
-  -- regular bindings to support multiple profile use. This is a little
-  -- weird with the KeyBound dialog box (which has per-char selector as well
-  -- as an OK/Cancel box) but it's the least amount of effort to implement.
-  function GetActionName(f)
-    local b = buttonLookup[f]
-    if b then
-      return format("%s:%s", b.bar:GetName(), b.idx)
-    end
-  end
-
-  function GetHotkey(f)
-    local b = buttonLookup[f]
-    if b then
-      return KB:ToShortKey(b:GetConfig().hotkey)
-    end
-  end
-
-  function SetKey(f, key)
-    local b = buttonLookup[f]
-    if b then
-      local c = b:GetConfig()
-      if c.hotkey then
-        SetOverrideBinding(f, false, c.hotkey, nil)
-      end
-      if key then
-        SetOverrideBindingClick(f, false, key, f:GetName(), nil)
-      end
-      c.hotkey = key
-      b:DisplayHotkey(GetHotkey(f))
-    end
-  end
-
-  function FreeKey(f, key)
-    local b = buttonLookup[f]
-    if b then
-      local c = b:GetConfig()
-      if c.hotkey == key then
-        local action = f:GetActionName()
-        SetOverrideBinding(f, false, c.hotkey, nil)
-        c.hotkey = nil
-        b:DisplayHotkey(nil)
-        return action
-      end
-    end
-    return ReAction:FreeOverrideHotkey(key)
-  end
-
-  function ClearBindings(f)
-    SetKey(f, nil)
-  end
-
-  function GetBindings(f)
-    local b = buttonLookup[f]
-    if b then
-      return b:GetConfig().hotkey
-    end
-  end
-
-  function KBAttach( button )
-    local f = button:GetFrame()
-    f.GetActionName = GetActionName
-    f.GetHotkey     = GetHotkey
-    f.SetKey        = SetKey
-    f.FreeKey       = FreeKey
-    f.ClearBindings = ClearBindings
-    f.GetBindings   = GetBindings
-    buttonLookup[f] = button
-    f:SetKey(button:GetConfig().hotkey)
-    ReAction:RegisterKeybindFrame(f)
-    if ReAction:GetKeybindMode() then
-      button.border:SetVertexColor(KB:GetColorKeyBoundMode())
-      button.border:Show()
-    end
-  end
-
-  function OnEnter( self )
-    if not self.tooltipName then
-      return;
-    end
-    local uber = GetCVar("UberTooltips")
-    if self.isToken or (uber == "0") then
-      if uber == "0" then
-        GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
-      else
-        GameTooltip_SetDefaultAnchor(GameTooltip, self)
-      end
-      local tooltip = self.tooltipName
-      local k = GetBindings(self)
-      if k then
-        tooltip = tooltip .. format(" %s(%s)%s", NORMAL_FONT_COLOR_CODE, k, FONT_COLOR_CODE_CLOSE)
-      end
-      GameTooltip:SetText(tooltip)
-      if self.tooltipSubtext then
-        GameTooltip:AddLine(self.tooltipSubtext, "", 0.5, 0.5, 0.5)
-      end
-      GameTooltip:Show()
-    else
-      GameTooltip_SetDefaultAnchor(GameTooltip, self)
-      GameTooltip:SetPetAction(self:GetID())
-    end
-  end
-
-  function OnLeave()
-    GameTooltip:Hide()
-  end
-
-end
-
-local meta = { __index = Button }
-
-function Button:New( bar, idx, config )
-  -- create new self
-  self = setmetatable( 
-    { 
-      bar = bar,
-      idx = idx,
-      config = config,
-    }, meta )
-
-  local name = config.name or ("ReAction_%s_%s_%d"):format(bar:GetName(),moduleID,idx)
-  config.name = name
-  self.name = name
-  config.actionID = ActionIDList[config.actionID] -- gets a free one if none configured
-  
-  -- have to recycle frames with the same name:
-  -- otherwise you either get references to old textures because named CreateFrame()
-  -- doesn't overwrite existing globals. Can't set them to nil in the global table, 
-  -- as it causes taint.
-  local parent = bar:GetFrame()
-  local f = frameRecycler[name]
-  if f then
-    f:SetParent(parent)
-  else
-    f = CreateFrame("CheckButton", name, parent, "PetActionButtonTemplate")
-    -- ditch the old hotkey text because it's tied in ActionButton_Update() to the
-    -- standard binding. We use override bindings.
-    local hotkey = _G[name.."HotKey"]
-    hotkey:SetParent(trash)
-    hotkey = f:CreateFontString(nil, "ARTWORK", "NumberFontNormalSmallGray")
-    hotkey:SetWidth(36)
-    hotkey:SetHeight(18)
-    hotkey:SetJustifyH("RIGHT")
-    hotkey:SetJustifyV("TOP")
-    hotkey:SetPoint("TOPLEFT",f,"TOPLEFT",-2,-2)
-    f.hotkey = hotkey
-    f:HookScript("OnDragStart", function() self:Update() end)
-    f:HookScript("OnReceiveDrag", function() self:Update() end)
-    f:SetScript("OnEnter", OnEnter)
-    f:SetScript("OnLeave", OnLeave)
-  end
-  if config.actionID then
-    f:SetID(config.actionID) -- PetActionButtonTemplate isn't a proper SecureActionButton
-  end
-  f:SetFrameStrata("MEDIUM")
-  self.frame    = f
-  self.icon     = _G[("%sIcon"):format(name)]
-  self.acTex    = _G[("%sAutoCastable"):format(name)]
-  self.acModel  = _G[("%sShine"):format(name)]
-  self.cooldown = _G[("%sCooldown"):format(name)]
-  self.hotkey   = f.hotkey
-  self.border   = _G[("%sBorder"):format(name)]
-
-
-  f:RegisterEvent("PLAYER_CONTROL_LOST");
-	f:RegisterEvent("PLAYER_CONTROL_GAINED");
-	f:RegisterEvent("PLAYER_FARSIGHT_FOCUS_CHANGED");
-	f:RegisterEvent("UNIT_PET");
-	f:RegisterEvent("UNIT_FLAGS");
-	f:RegisterEvent("UNIT_AURA");
-	f:RegisterEvent("PET_BAR_UPDATE");
-	f:RegisterEvent("PET_BAR_UPDATE_COOLDOWN");
-
-  f:SetScript("OnEvent",
-    function(event,arg1)
-      if event =="PET_BAR_UPDATE_COOLDOWN" then
-        self:UpdateCooldown()
-      elseif event == "UPDATE_BINDINGS" then
-        self:UpdateHotkey()
-      else
-        self:Update()
-      end
-    end)
-
-  -- install drag wrappers to lock buttons 
-  bar:GetFrame():WrapScript(f, "OnDragStart",
-    -- OnDragStart(self, button, kind, value, ...)
-    [[
-      if lockButtons and (PlayerInCombat() or not lockButtonsCombat) and not IsModifiedClick("PICKUPACTION") then
-        return "clear"
-      end
-    ]])
-
-  KBAttach(self)
-
-  -- attach to skinner
-  bar:SkinButton(self,
-    {
-      HotKey = self.hotkey,
-    }
-  )
-
-  self:Refresh()
-  self:SetKeybindMode(ReAction:GetKeybindMode())
-
-  return self
-end
-
-function Button:Destroy()
-  local f = self.frame
-  f:UnregisterAllEvents()
-  f:Hide()
-  f:SetParent(UIParent)
-  f:ClearAllPoints()
-  if self.name then
-    frameRecycler[self.name] = f
-    _G[self.name] = nil
-  end
-  if self.config.actionID then
-    ActionIDList[self.config.actionID] = nil
-  end
-  self.frame = nil
-  self.config = nil
-  self.bar = nil
-end
-
-function Button:Refresh()
-  self.bar:PlaceButton(self, 30, 30)
-  self:Update()
-  self:UpdateHotkey()
-  self.frame:Show()
-end
-
-function Button:GetFrame()
-  return self.frame
-end
-
-function Button:GetName()
-  return self.name
-end
-
-function Button:GetConfig()
-  return self.config
-end
-
-function Button:GetActionID()
-  return self.config.actionID
-end
-
-function Button:Update()
-  local id = self.frame:GetID()
-  local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(id);
-  local f = self.frame
-  --ReAction:Print(("id %d: '%s', '%s', '%s', '%s', '%s', '%s', '%s'"):format(tostring(id), tostring(name),tostring(subtext),tostring(texture),tostring(isToken),tostring(isActive),tostring(autoCastAllowed),tostring(autoCastEnabled)))
-
-  if isToken then
-    self.icon:SetTexture(_G[texture]);
-    f.tooltipName = _G[name];
-  else
-    self.icon:SetTexture(texture);
-    f.tooltipName = name;
-  end
-
-  f.isToken = isToken;
-	f.tooltipSubtext = subtext;
-  f:SetChecked( isActive and 1 or 0);
-
-  if autoCastAllowed then
-    self.acTex:Show();
-  else
-    self.acTex:Hide();
-  end
-
-  if autoCastEnabled then
-    AutoCastShine_AutoCastStart(self.acModel)
-  else
-    AutoCastShine_AutoCastStop(self.acModel)
-  end
-
-  if texture then
-    if GetPetActionSlotUsable(id) then
-      SetDesaturation(self.icon,nil)
-    else
-      SetDesaturation(self.icon,1)
-    end
-    self.icon:Show();
-    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2");
-  else
-    self.icon:Hide();
-    f:SetNormalTexture("Interface\\Buttons\\UI-Quickslot");
-  end
-
-  self:UpdateCooldown()
-end
-
-function Button:UpdateCooldown()
-	local start, duration, enable = GetPetActionCooldown(self.frame:GetID());
-  CooldownFrame_SetTimer(self.cooldown, start, duration, enable);
-end
-
-function Button:UpdateHotkey()
-  self:DisplayHotkey(GetHotkey(self.frame))
-end
-
-function Button:ShowActionIDLabel(show)
-  if show then
-    -- store the action ID label in the frame due to frame recycling
-    if not self.actionIDLabel and self:GetActionID() then
-      local label = self.frame:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
-      label:SetAllPoints()
-      label:SetJustifyH("CENTER")
-      label:SetShadowColor(0,0,0,1)
-      label:SetShadowOffset(2,-2)
-      label:SetText(tostring(self:GetActionID()))
-      self.actionIDLabel = label
-    end
-    self.actionIDLabel:Show()
-  elseif self.actionIDLabel then
-    self.actionIDLabel:Hide()
-  end
-end
-
-
-function Button:SetKeybindMode(mode)
-  if mode then
-    self.border:SetVertexColor(KB:GetColorKeyBoundMode())
-    self.border:Show()
-  else
-    self.border:Hide()
-  end
-end
-
-function Button:DisplayHotkey( key )
-  self.hotkey:SetText(key or "")
-end
--- a/modules/ReAction_PetAction/ReAction_PetAction.toc	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,11 +0,0 @@
-## Interface: 30000
-## Title: ReAction: Pet Actions
-## Notes: Pet Action Bar support for ReAction
-## DefaultState: enabled
-## LoadOnDemand: 1
-## Author: Flick
-## Version: 1.0
-## X-Category: Action Bars
-## Dependencies: ReAction
-
-ReAction_PetAction.xml
--- a/modules/ReAction_PetAction/ReAction_PetAction.xml	Wed Jan 07 00:57:02 2009 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<Ui xmlns="http://www.blizzard.com/wow/ui/" 
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
-    xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
-
-<Script file="ReAction_PetAction.lua"/>
-
-</Ui>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/State.lua	Thu Jan 08 00:57:27 2009 +0000
@@ -0,0 +1,1338 @@
+--[[
+  ReAction bar state driver interface
+
+--]]
+
+-- local imports
+local ReAction = ReAction
+local L = ReAction.L
+local _G = _G
+local format = string.format
+local InCombatLockdown = InCombatLockdown
+local RegisterStateDriver = RegisterStateDriver
+
+ReAction:UpdateRevision("$Revision$")
+
+-- module declaration
+local moduleID = "State"
+local module = ReAction:NewModule( moduleID, "AceEvent-3.0" )
+
+-- Utility --
+
+-- traverse a table tree by key list and fetch the result or first nil
+local function tfetch(t, ...)
+  for i = 1, select('#', ...) do
+    t = t and t[select(i, ...)]
+  end
+  return t
+end
+
+-- traverse a table tree by key list and build tree as necessary
+local function tbuild(t, ...)
+  for i = 1, select('#', ...) do
+    local key = select(i, ...)
+    if not t[key] then t[key] = { } end
+    t = t[key]
+  end
+  return t
+end
+
+-- return a new array of keys of table 't', sorted by comparing 
+-- sub-fields (obtained via tfetch) of the table values
+local function fieldsort( t, ... )
+  local r = { }
+  for k in pairs(t) do
+    table.insert(r,k)
+  end
+  local path = { ... }
+  table.sort(r, function(lhs, rhs)
+     local olhs = tfetch(t[lhs], unpack(path)) or 0
+     local orhs = tfetch(t[rhs], unpack(path)) or 0
+     return olhs < orhs
+    end)
+  return r
+end
+
+-- set a frame-ref, if the frame is valid, or set nil to the
+-- corresponding attribute
+local function SetFrameRef(frame, name, refFrame)
+  if refFrame then
+    local _, explicit = refFrame:IsProtected()
+    if not explicit then
+      refFrame = nil
+    end
+  end
+  if refFrame then
+    frame:SetFrameRef(name,refFrame)
+  else
+    frame:SetAttribute("frameref-"..name,nil)
+  end
+end
+
+
+local InitRules, ApplyStates, CleanupStates, SetProperty, GetProperty, RegisterProperty, ShowAll
+
+-- PRIVATE --
+do
+
+  -- the field names must match the field names of the options table, below
+  -- the field values are secure snippets or 'true' to skip the snippet for that property.
+  local properties = { 
+    hide = 
+      [[
+        local h = hide and hide[state] and not showAll
+        if h ~= hidden then
+          if h then
+            self:Hide()
+          else
+            self:Show()
+          end
+          hidden = h
+        end
+        if showAll then
+          control:CallMethod("UpdateHiddenLabel", hide and hide[state])
+        end
+      ]],
+
+    --keybindState  TODO: broken
+
+    anchorEnable = 
+      [[
+        local old_anchor = anchorstate
+        anchorstate = (anchorEnable and anchorEnable[state]) and state
+        if old_anchor ~= anchorstate or not set_state then
+          if anchorstate and anchorPoint then
+            if anchorPoint[state] then
+              self:ClearAllPoints()
+              local f = self:GetAttribute("frameref-anchor-"..anchorstate)
+              if f then
+                self:SetPoint(anchorPoint[state], f, anchorRelPoint[state], anchorX[state], anchorY[state])
+              end
+            end
+          elseif defaultAnchor and defaultAnchor.point then
+            self:ClearAllPoints()
+            self:SetPoint(defaultAnchor.point, defaultAnchor.frame, 
+                          defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
+          end
+        end
+      ]],
+      -- anchorEnable handles all the other bits
+    anchorFrame = true,
+    anchorPoint = true,
+    anchorRelPoint = true,
+    anchorX = true,
+    anchorY = true,
+
+
+    enableScale = 
+      [[
+        local old_scale = scalestate
+        scalestate = (enableScale and enableScale[state]) and state
+        if old_scale ~= scalestate or not set_state then
+          if scalestate and scale then
+            if scale[state] then
+              self:SetScale(scale[state])
+            end
+          else
+            self:SetScale(1.0)
+          end
+        end
+      ]],
+      -- enableScale handles scale
+    scale = true,
+
+    enableAlpha = 
+      [[
+        local old_alpha = alphastate
+        alphastate = (enableAlpha and enableAlpha[state]) and state
+        if old_alpha ~= alphastate or not set_state then
+          control:CallMethod("UpdateAlpha", alphastate and alpha[state] or defaultAlpha)
+        end
+      ]],
+      -- enableAlpha handles alpha
+    alpha = true,
+  }
+
+  local weak         = { __mode = "k" }
+  local statedrivers = setmetatable( { }, weak )
+  local keybinds     = setmetatable( { }, weak )
+
+  --
+  -- Secure Handler Snippets
+  --
+  local SetHandlerData, SetStateDriver, SetStateKeybind, RefreshState
+  do
+    local stateHandler_propInit = 
+    [[
+      propfuncs = table.new()
+      local proplist = self:GetAttribute("prop-func-list")
+      for s in string.gmatch(proplist, "(%w+)") do
+        table.insert(propfuncs, s)
+      end
+    ]]
+
+    local onStateHandler = 
+    -- function _onstate-reaction( self, stateid, newstate )
+    [[
+      set_state = newstate
+
+      local oldState = state
+      state = state_override or set_state or state
+      for i = 1, #propfuncs do
+        control:RunAttribute("func-"..propfuncs[i])
+      end
+      
+      control:ChildUpdate()
+
+      if oldState ~= state then
+        control:CallMethod("StateRefresh", state)
+      end
+    ]]
+
+    local onClickHandler = 
+    -- function OnClick( self, button, down )
+    [[
+      if state_override == button then
+        state_override = nil -- toggle
+      else
+        state_override = button
+      end
+    ]] .. onStateHandler
+
+    local function UpdateAlpha( frame, alpha )
+      if alpha then
+        frame:SetAlpha(alpha)
+      end
+    end
+
+    -- Construct a lua assignment as a code string and execute it within the header
+    -- frame's sandbox. 'value' must be a string, boolean, number, or nil. If called
+    -- with four arguments, then it treats 'varname' as an existing global table and
+    -- sets a key-value pair. For a slight efficiency boost, pass the values in as
+    -- attributes and fetch them as attributes from the snippet code, to leverage snippet
+    -- caching.
+    function SetHandlerData( bar, varname, value, key )
+      local f = bar:GetFrame()
+      f:SetAttribute("data-varname",varname)
+      f:SetAttribute("data-value",  value)
+      f:SetAttribute("data-key",    key)
+      f:Execute(
+        [[
+          local name  = self:GetAttribute("data-varname")
+          local value = self:GetAttribute("data-value")
+          local key   = self:GetAttribute("data-key")
+          if name then
+            if key then
+              if not _G[name] then
+                _G[name] = table.new()
+              end
+              _G[name][key] = value
+            else
+              _G[name] = value
+            end
+          end
+        ]])
+    end
+
+    function SetDefaultAnchor( bar )
+      local point, frame, relPoint, x, y = bar:GetAnchor()
+      SetHandlerData(bar, "defaultAnchor", point, "point")
+      SetHandlerData(bar, "defaultAnchor", relPoint, "relPoint")
+      SetHandlerData(bar, "defaultAnchor", x, "x")
+      SetHandlerData(bar, "defaultAnchor", y, "y")
+      SetHandlerData(bar, "defaultAlpha",  bar:GetAlpha())
+
+      local f = bar:GetFrame()
+      f.UpdateAlpha = UpdateAlpha
+      SetFrameRef(f, "defaultAnchor", _G[frame or "UIParent"])
+      f:Execute(
+        [[
+          defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
+        ]])
+    end
+
+    function RefreshState( bar )
+      SetDefaultAnchor(bar)
+      bar:GetFrame():Execute(
+        [[
+          if self:GetAttribute("reaction-refresh") then
+            control:RunAttribute("reaction-refresh")
+          end
+        ]])
+    end
+
+    function SetStateDriver( bar, rule )
+      local f = bar:GetFrame()
+
+      if not f.UpdateHiddenLabel then
+        function f:UpdateHiddenLabel(hide)
+          bar:SetLabelSubtext( hide and L["Hidden"] )
+        end
+      end
+
+      function f:StateRefresh( state )
+        bar:RefreshControls()
+      end
+
+      local props = { }
+      for p, h in pairs(properties) do
+        if type(h) == "string" then
+          table.insert(props,p)
+          f:SetAttribute("func-"..p, h)
+        end
+      end
+      f:SetAttribute("prop-func-list", table.concat(props," "))
+      f:Execute(stateHandler_propInit)
+      f:SetAttribute("reaction-refresh", onStateHandler)
+      
+      if rule and #rule > 0 then
+        f:SetAttribute( "_onstate-reaction", onStateHandler )
+        RegisterStateDriver(f, "reaction", rule)
+        statedrivers[bar] = rule
+      elseif statedrivers[bar] then
+        UnregisterStateDriver(f, "reaction")
+        f:SetAttribute( "_onstate-reaction", nil )
+        statedrivers[bar] = nil
+      end
+    end
+
+    function SetStateKeybind( bar, key, state )
+      local f = bar:GetFrame()
+
+      local kb = keybinds[bar]
+      if kb == nil then
+        if key == nil then
+          -- nothing to do
+          return
+        end
+        kb = { }
+        keybinds[bar] = kb
+      end
+
+      -- clear the old binding, if any
+      if kb[state] then
+        SetOverrideBinding(f, false, kb[state], nil)
+      end
+      kb[state] = key
+
+      if key then
+        f:SetAttribute("_onclick", onClickHandler)
+        SetOverrideBindingClick(f, false, key, state, nil) -- state name is the virtual mouse button
+      end
+    end
+  end
+
+  -- As far as I can tell the macro clauses are NOT locale-specific.
+  local ruleformats = { 
+    stealth       = "stealth",
+    nostealth     = "nostealth",
+    shadowform    = "form:1",
+    noshadowform  = "noform",
+    pet           = "pet",
+    nopet         = "nopet",
+    harm          = "target=target,harm",
+    help          = "target=target,help",
+    notarget      = "target=target,noexists",
+    focusharm     = "target=focus,harm",
+    focushelp     = "target=focus,help",
+    nofocus       = "target=focus,noexists",
+    raid          = "group:raid",
+    party         = "group:party",
+    solo          = "nogroup",
+    combat        = "combat",
+    nocombat      = "nocombat",
+    possess       = "bonusbar:5",
+  }
+
+  -- Have to do these shenanigans instead of hardcoding the stances/forms because the 
+  -- ordering varies if the character is missing a form. For warriors this is rarely
+  -- a problem (c'mon, who actually skips the level 10 def stance quest?) but for druids
+  -- it can be. Some people never bother to do the aquatic form quest until well past
+  -- when they get cat form, and stance 5/6 can be flight, tree, or moonkin depending
+  -- on talents.
+  function InitRules()
+    local forms = { }
+      -- sort by icon since it's locale-independent
+    for i = 1, GetNumShapeshiftForms() do
+      local icon, name, active = GetShapeshiftFormInfo(i)
+      -- if it's the current form, the icon is wrong (Ability_Spell_WispSplode)
+      -- so capture it from the spell info directly
+      if active then
+        local _1, _2
+        _1, _2, icon = GetSpellInfo(name)
+      end
+      forms[icon] = i;
+    end
+      -- use 9 if not found since 9 is never a valid stance/form
+    local defensive = forms["Interface\\Icons\\Ability_Warrior_DefensiveStance"] or 9
+    local berserker = forms["Interface\\Icons\\Ability_Racial_Avatar"] or 9
+    local bear      = forms["Interface\\Icons\\Ability_Racial_BearForm"] or 9 -- bear and dire bear share the same icon
+    local aquatic   = forms["Interface\\Icons\\Ability_Druid_AquaticForm"] or 9
+    local cat       = forms["Interface\\Icons\\Ability_Druid_CatForm"] or 9
+    local travel    = forms["Interface\\Icons\\Ability_Druid_TravelForm"] or 9
+    local tree      = forms["Interface\\Icons\\Ability_Druid_TreeofLife"] or 9
+    local moonkin   = forms["Interface\\Icons\\Spell_Nature_ForceOfNature"] or 9
+    local flight    = forms["Interface\\Icons\\Ability_Druid_FlightForm"] or 9 -- flight and swift flight share the same icon
+
+    ruleformats.battle    = "stance:1"
+    ruleformats.defensive = format("stance:%d",defensive)
+    ruleformats.berserker = format("stance:%d",berserker)
+    ruleformats.caster    = format("form:0/%d/%d/%d",aquatic, travel, flight)
+    ruleformats.bear      = format("form:%d",bear)
+    ruleformats.cat       = format("form:%d",cat)
+    ruleformats.tree      = format("form:%d",tree)
+    ruleformats.moonkin   = format("form:%d",moonkin)
+  end
+
+  local function BuildRule(states)
+    local rules = { }
+    local default
+
+    for idx, state in ipairs(fieldsort(states, "rule", "order")) do
+      local c = states[state].rule
+      local type = c.type
+      if type == "default" then
+        default = default or state
+      elseif type == "custom" then
+        if c.custom then
+          -- strip out all spaces from the custom rule
+          table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
+        end
+      elseif type == "any" or type == "all" then
+        if c.values then
+          local clauses = { }
+          for key, value in pairs(c.values) do
+            table.insert(clauses, ruleformats[key])
+          end
+          if #clauses > 0 then
+            local sep = (type == "any") and "][" or ","
+            table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
+          end
+        end
+      end
+    end
+    -- make sure that the default, if any, is last
+    if default then
+      table.insert(rules, default)
+    end
+    return table.concat(rules,";")
+  end
+
+  local function BuildKeybinds( bar, states )
+    for name, state in pairs(states) do
+      local type = tfetch(state, "rule", "type")
+      if type == "keybind" then
+        local key = tfetch(state, "rule", "keybind")
+        SetStateKeybind(bar, key, name)
+      else
+        SetStateKeybind(bar, nil, name) -- this clears an existing keybind
+      end
+    end
+  end
+
+  function GetProperty( bar, state, propname )
+    return tfetch(module.db.profile.bars, bar:GetName(), "states", state, propname)
+  end
+
+  function SetProperty( bar, state, propname, value )
+    local s = tbuild(module.db.profile.bars, bar:GetName(), "states", state)
+    s[propname] = value
+    SetHandlerData(bar, propname, value, state)
+    RefreshState(bar)
+  end
+
+  function RegisterProperty( propname, snippet )
+    properties[propname] = snippet or true
+    for _, bar in ReAction:IterateBars() do
+      local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
+      if states then
+        for name, s in pairs(states) do
+          SetHandlerData(bar, propname, s[propname], name)
+        end
+        SetStateDriver(bar, BuildRule(states))
+        RefreshState(bar)
+      end
+    end
+  end
+
+  function UnregisterProperty( propname )
+    properties[propname] = nil
+    for _, bar in ReAction:IterateBars() do
+      SetHandlerData(bar, propname, nil)
+      SetStateDriver(bar, BuildRule(states))
+      RefreshState(bar)
+    end
+  end
+
+  function ApplyStates( bar )
+    local states = tfetch(module.db.profile.bars, bar:GetName(), "states")
+    if states then
+      for propname in pairs(properties) do
+        for name, s in pairs(states) do
+          if propname == "anchorFrame" then
+            SetFrameRef(bar:GetFrame(), "anchor-"..name, _G[s.anchorFrame])
+          else
+            SetHandlerData(bar, propname, s[propname], name)
+          end
+        end
+      end
+      BuildKeybinds(bar, states)
+      SetHandlerData(bar, "showAll", ReAction:GetConfigMode())
+      SetStateDriver(bar, BuildRule(states))
+      RefreshState(bar)
+    end
+  end
+
+  function CleanupStates( bar )
+    SetStateDriver(bar, nil)
+  end
+
+  function ShowAll( bar, show )
+    if statedrivers[bar] then
+      SetHandlerData(bar, "showAll", show)
+      RefreshState(bar)
+    end
+  end
+end
+
+
+
+-- module event handlers --
+
+function module:OnInitialize()
+  self.db = ReAction.db:RegisterNamespace( moduleID, 
+    {
+      profile = { 
+        bars = { },
+      }
+    }
+  )
+
+  self:RegisterEvent("UPDATE_SHAPESHIFT_FORMS")
+
+  ReAction:RegisterBarOptionGenerator(self, "GetBarOptions")
+
+  ReAction.RegisterCallback(self, "OnCreateBar","OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnDestroyBar")
+  ReAction.RegisterCallback(self, "OnRefreshBar")
+  ReAction.RegisterCallback(self, "OnEraseBar")
+  ReAction.RegisterCallback(self, "OnRenameBar")
+  ReAction.RegisterCallback(self, "OnConfigModeChanged")
+end
+
+function module:OnEnable()
+  self:UPDATE_SHAPESHIFT_FORMS() -- it doesn't fire on a /reloadui
+end
+
+function module:UPDATE_SHAPESHIFT_FORMS()
+  -- Re-parse the rules table according to the new form list.
+  -- This happens both at initial login (after PLAYER_ENTERING_WORLD)
+  -- as well as when gaining new abilities. 
+  InitRules()
+  for name, bar in ReAction:IterateBars() do
+    self:OnRefreshBar(nil,bar,name)
+  end
+end
+
+function module:OnRefreshBar(event, bar, name)
+  local c = self.db.profile.bars[name]
+  if c then
+    ApplyStates(bar)
+  end
+end
+
+function module:OnDestroyBar(event, bar, name)
+  CleanupStates(bar)
+end
+
+function module:OnEraseBar(event, bar, name)
+  self.db.profile.bars[name] = nil
+end
+
+function module:OnRenameBar(event, bar, oldname, newname)
+  local bars = self.db.profile.bars
+  bars[newname], bars[oldname] = bars[oldname], nil
+end
+
+function module:OnConfigModeChanged(event, mode)
+  for name, bar in ReAction:IterateBars() do
+    if self.db.profile.bars[name] then
+      ShowAll(bar, mode)
+    end
+  end
+end
+
+
+
+-- Options --
+
+local CreateBarOptions, RegisterPropertyOptions
+do
+  local playerClass = select(2, UnitClass("player"))
+  local function ClassCheck(...)
+    for i = 1, select('#',...) do
+      if playerClass == select(i,...) then
+        return false
+      end
+    end
+    return true
+  end
+
+  -- pre-sorted by the order they should appear in
+  local rules = {
+    --  rule          hidden                          fields
+    { "stance",  ClassCheck("WARRIOR"),          { {battle = L["Battle Stance"]}, {defensive = L["Defensive Stance"]}, {berserker = L["Berserker Stance"]} } },
+    { "form",    ClassCheck("DRUID"),            { {caster = L["Caster Form"]}, {bear = L["Bear Form"]}, {cat = L["Cat Form"]}, {tree = L["Tree of Life"]}, {moonkin = L["Moonkin Form"]} } },
+    { "stealth", ClassCheck("ROGUE","DRUID"),    { {stealth = L["Stealth"]}, {nostealth = L["No Stealth"]} } },
+    { "shadow",  ClassCheck("PRIEST"),           { {shadowform = L["Shadowform"]}, {noshadowform = L["No Shadowform"]} } },
+    { "pet",     ClassCheck("HUNTER","WARLOCK"), { {pet = L["With Pet"]}, {nopet = L["Without Pet"]} } },
+    { "target",  false,                          { {harm = L["Hostile Target"]}, {help = L["Friendly Target"]}, {notarget = L["No Target"]} } },
+    { "focus",   false,                          { {focusharm = L["Hostile Focus"]}, {focushelp = L["Friendly Focus"]}, {nofocus = L["No Focus"]} } },
+    { "possess", false,                          { {possess = L["Mind Control"]} } },
+    { "group",   false,                          { {raid = L["Raid"]}, {party = L["Party"]}, {solo = L["Solo"]} } },
+    { "combat",  false,                          { {combat = L["In Combat"]}, {nocombat = L["Out of Combat"]} } },
+  }
+
+  local ruleSelect = { }
+  local ruleMap    = { }
+  local optionMap  = setmetatable({},{__mode="k"})
+
+  local pointTable = {
+    NONE        = " ",
+    CENTER      = L["Center"], 
+    LEFT        = L["Left"],
+    RIGHT       = L["Right"],
+    TOP         = L["Top"],
+    BOTTOM      = L["Bottom"],
+    TOPLEFT     = L["Top Left"],
+    TOPRIGHT    = L["Top Right"],
+    BOTTOMLEFT  = L["Bottom Left"],
+    BOTTOMRIGHT = L["Bottom Right"],
+  }
+
+  -- unpack rules table into ruleSelect and ruleMap
+  for _, c in ipairs(rules) do
+    local rule, hidden, fields = unpack(c)
+    if not hidden then
+      for _, field in ipairs(fields) do
+        local key, label = next(field)
+        table.insert(ruleSelect, label)
+        table.insert(ruleMap, key)
+      end
+    end
+  end
+
+  local stateOptions = {
+    ordering = {
+      name = L["Info"],
+      order = 1,
+      type = "group",
+      args = {
+        delete = {
+          name = L["Delete this State"],
+          order = -1,
+          type = "execute",
+          func = "DeleteState",
+        },
+        rename = {
+          name = L["Name"],
+          order = 1,
+          type = "input",
+          get  = "GetName",
+          set  = "SetStateName",
+          pattern = "^%w*$",
+          usage = L["State names must be alphanumeric without spaces"],
+        },
+        ordering = {
+          name = L["Evaluation Order"],
+          desc = L["State transitions are evaluated in the order listed:\nMove a state up or down to change the order"],
+          order = 2,
+          type = "group",
+          inline = true,
+          args = {
+            up = {
+              name  = L["Up"],
+              order = 1,
+              type  = "execute",
+              width = "half",
+              func  = "MoveStateUp",
+            },
+            down = {
+              name  = L["Down"],
+              order = 2,
+              type  = "execute",
+              width = "half",
+              func  = "MoveStateDown",
+            }
+          }
+        }
+      }
+    },
+    properties = {
+      name = L["Properties"],
+      order = 2,
+      type = "group",
+      args = { 
+        desc = {
+          name = L["Set the properties for the bar when in this state"],
+          order = 1,
+          type = "description"
+        },
+        hide = {
+          name = L["Hide Bar"],
+          order = 90,
+          type = "toggle",
+          set  = "SetProp",
+          get  = "GetProp",
+        },
+        --[[ BROKEN
+        keybindState = {
+          name  = L["Override Keybinds"],
+          desc  = L["Set this state to maintain its own set of keybinds which override the defaults when active"],
+          order = 91,
+          type  = "toggle",
+          set   = "SetProp",
+          get   = "GetProp",
+        }, ]]
+        position = {
+          name  = L["Position"],
+          order = 92,
+          type  = "group",
+          inline = true,
+          args = {
+            anchorEnable = {
+              name  = L["Reposition"],
+              order = 1,
+              type  = "toggle",
+              set   = "SetProp",
+              get   = "GetProp",
+            },
+            anchorFrame = {
+              name   = L["Anchor Frame"],
+              order  = 2,
+              type   = "select",
+              values = "GetAnchorFrames",
+              set    = "SetAnchorFrame",
+              get    = "GetAnchorFrame",
+              disabled = "GetAnchorDisabled",
+              hidden = "GetAnchorDisabled",
+            },
+            anchorPoint = {
+              name  = L["Point"],
+              order = 3,
+              type  = "select",
+              values = pointTable,
+              set   = "SetAnchorPointProp",
+              get   = "GetAnchorPointProp",
+              disabled = "GetAnchorDisabled",
+              hidden = "GetAnchorDisabled",
+            },
+            anchorRelPoint = {
+              name  = L["Relative Point"],
+              order = 4,
+              type  = "select",
+              values = pointTable,
+              set   = "SetAnchorPointProp",
+              get   = "GetAnchorPointProp",
+              disabled = "GetAnchorDisabled",
+              hidden = "GetAnchorDisabled",
+            },
+            anchorX = {
+              name  = L["X Offset"],
+              order = 5,
+              type  = "range",
+              min   = -100,
+              max   = 100,
+              step  = 1,
+              set   = "SetProp",
+              get   = "GetProp",
+              disabled = "GetAnchorDisabled",
+              hidden = "GetAnchorDisabled",
+            },
+            anchorY = {
+              name  = L["Y Offset"],
+              order = 6,
+              type  = "range",
+              min   = -100,
+              max   = 100,
+              step  = 1,
+              set   = "SetProp",
+              get   = "GetProp",
+              disabled = "GetAnchorDisabled",
+              hidden = "GetAnchorDisabled",
+            },
+          },
+        },
+        scale = {
+          name  = L["Scale"],
+          order = 93,
+          type  = "group",
+          inline = true,
+          args = {
+            enableScale = {
+              name  = L["Set New Scale"],
+              order = 1,
+              type  = "toggle",
+              set   = "SetProp",
+              get   = "GetProp",
+            },
+            scale = {
+              name  = L["Scale"],
+              order = 2,
+              type  = "range",
+              min   = 0.25,
+              max   = 2.5,
+              step  = 0.05,
+              isPercent = true,
+              set   = "SetProp",
+              get   = "GetScale",
+              disabled = "GetScaleDisabled",
+              hidden = "GetScaleDisabled",
+            },
+          },
+        },
+        alpha = {
+          name  = L["Transparency"],
+          order = 94,
+          type  = "group",
+          inline = true,
+          args = {
+            enableAlpha = {
+              name  = L["Set Transparency"],
+              order = 1,
+              type  = "toggle",
+              set   = "SetProp",
+              get   = "GetProp",
+            },
+            alpha = {
+              name  = L["Transparency"],
+              order = 2,
+              type  = "range",
+              min   = 0,
+              max   = 1,
+              step  = 0.01,
+              bigStep = 0.05,
+              isPercent = true,
+              set   = "SetProp",
+              get   = "GetAlpha",
+              disabled = "GetAlphaDisabled",
+              hidden = "GetAlphaDisabled",
+            },
+          },
+        },
+      },
+      plugins = { }
+    },
+    rules = {
+      name   = L["Rule"],
+      order  = 3,
+      type   = "group",
+      args   = {
+        mode = {
+          name   = L["Select this state"],
+          order  = 2,
+          type   = "select",
+          style  = "radio",
+          values = { 
+            default = L["by default"], 
+            any = L["when ANY of these"], 
+            all = L["when ALL of these"], 
+            custom = L["via custom rule"],
+            keybind = L["via keybinding"],
+          },
+          set    = "SetType",
+          get    = "GetType",
+        },
+        clear = {
+          name     = L["Clear All"],
+          order    = 3,
+          type     = "execute",
+          hidden   = "GetClearAllDisabled",
+          disabled = "GetClearAllDisabled",
+          func     = "ClearAllConditions",
+        },
+        inputs = {
+          name     = L["Conditions"],
+          order    = 4,
+          type     = "multiselect",
+          hidden   = "GetConditionsDisabled",
+          disabled = "GetConditionsDisabled",
+          values   = ruleSelect,
+          set      = "SetCondition",
+          get      = "GetCondition",
+        },
+        custom = {
+          name = L["Custom Rule"],
+          order = 5,
+          type = "input",
+          multiline = true,
+          hidden = "GetCustomDisabled",
+          disabled = "GetCustomDisabled",
+          desc = L["Syntax like macro rules: see preset rules for examples"],
+          set  = "SetCustomRule",
+          get  = "GetCustomRule",
+          validate = "ValidateCustomRule",
+        },
+        keybind = {
+          name = L["Keybinding"],
+          order = 6,
+          inline = true,
+          hidden = "GetKeybindDisabled",
+          disabled = "GetKeybindDisabled",
+          type = "group",
+          args = {
+            desc = {
+              name = L["Invoking a state keybind toggles an override of all other transition rules."],
+              order = 1,
+              type = "description",
+            },
+            keybind = {
+              name = L["State Hotkey"],
+              desc = L["Define an override toggle keybind"],
+              order = 2,
+              type = "keybinding",
+              set  = "SetKeybind",
+              get  = "GetKeybind",
+            },
+          },
+        },
+      },
+    },
+  }
+
+  local handlers = { }
+  local meta = {
+    __index = function(self, key)
+      for _, h in pairs(handlers) do
+        if h[key] then
+          return h[key]
+        end
+      end
+    end,
+  }
+  local StateHandler = setmetatable({ }, meta)
+  local proto        = { __index = StateHandler }
+
+  function RegisterPropertyOptions( field, options, handler )
+    stateOptions.properties.plugins[field] = options
+    handlers[field] = handler
+  end
+
+  function UnregisterPropertyOptions( field )
+    stateOptions.properties.plugins[field] = nil
+    handlers[field] = nil
+  end
+
+  function StateHandler:New( bar, opts )
+    local self = setmetatable(
+      { 
+        bar = bar 
+      }, 
+      proto )
+
+    function self:GetName()
+      return opts.name
+    end
+
+    function self:SetName(name)
+      opts.name = name
+    end
+
+    function self:GetOrder()
+      return opts.order
+    end
+
+    -- get reference to states table: even if the bar
+    -- name changes the states table ref won't
+    self.states = tbuild(module.db.profile.bars, bar:GetName(), "states")
+    self.state  = tbuild(self.states, opts.name)
+
+    opts.order = self:GetRuleField("order")
+    if opts.order == nil then
+      -- add after the highest
+      opts.order = 100
+      for _, state in pairs(self.states) do
+        local x = tonumber(tfetch(state, "rule", "order"))
+        if x and x >= opts.order then
+          opts.order = x + 1
+        end
+      end
+      self:SetRuleField("order",opts.order)
+    end
+
+    return self
+  end
+
+  -- helper methods
+
+  function StateHandler:SetRuleField( key, value, ... )
+    tbuild(self.state, "rule", ...)[key] = value
+  end
+
+  function StateHandler:GetRuleField( ... )
+    return tfetch(self.state, "rule", ...)
+  end
+
+  function StateHandler:FixAll( setkey )
+    -- if multiple selections in the same group are chosen when 'all' is selected,
+    -- keep only one of them. If changing the mode, the first in the fields list will 
+    -- be chosen arbitrarily. Otherwise, if selecting a new checkbox from the field-set,
+    -- it will be retained.
+    local notified = false
+    if self:GetRuleField("type") == "all" then
+      for _, c in ipairs(rules) do
+        local rule, hidden, fields = unpack(c)
+        local once = false
+        if setkey then
+          for idx, field in ipairs(fields) do
+            if next(field) == setkey then
+              once = true
+            end
+          end
+        end
+        for idx, field in ipairs(fields) do
+          local key = next(field)
+          if self:GetRuleField("values",key) then
+            if once and key ~= setkey then
+              self:SetRuleField(key,false,"values")
+              if not setkey and not notified then
+                ReAction:UserError(L["Warning: one or more incompatible rules were turned off"])
+                notified = true
+              end
+            end
+            once = true
+          end
+        end
+      end
+    end
+  end
+
+  function StateHandler:GetNeighbors()
+    local before, after
+    for k, v in pairs(self.states) do
+      local o = tonumber(tfetch(v, "rule", "order"))
+      if o and k ~= self:GetName() then
+        local obefore = tfetch(self.states,before,"rule","order")
+        local oafter  = tfetch(self.states,after,"rule","order")
+        if o < self:GetOrder() and (not obefore or obefore < o) then
+          before = k
+        end
+        if o > self:GetOrder() and (not oafter or oafter > o) then
+          after = k
+        end
+      end
+    end
+    return before, after
+  end
+
+  function StateHandler:SwapOrder( a, b )
+    -- do options table
+    local args = optionMap[self.bar].args
+    args[a].order, args[b].order = args[b].order, args[a].order
+    -- do profile
+    a = tbuild(self.states, a, "rule")
+    b = tbuild(self.states, b, "rule")
+    a.order, b.order = b.order, a.order
+  end
+
+  -- handler methods 
+
+  function StateHandler:GetProp( info )
+    -- gets property of the same name as the options arg
+    return GetProperty(self.bar, self:GetName(), info[#info])
+  end
+
+  function StateHandler:SetProp( info, value )
+    -- sets property of the same name as the options arg
+    SetProperty(self.bar, self:GetName(), info[#info], value)
+  end
+
+  function StateHandler:DeleteState()
+    if self.states[self:GetName()] then
+      self.states[self:GetName()] = nil
+      ApplyStates(self.bar)
+    end
+    optionMap[self.bar].args[self:GetName()] = nil
+  end
+
+  function StateHandler:SetStateName(info, value)
+    -- check for existing state name
+    if self.states[value] then
+      ReAction:UserError(format(L["State named '%s' already exists"],value))
+      return
+    end
+    local args = optionMap[self.bar].args
+    local name = self:GetName()
+    self.states[value], args[value], self.states[name], args[name] = self.states[name], args[name], nil, nil
+    self:SetName(value)
+    ApplyStates(self.bar)
+    ReAction:ShowEditor(self.bar, moduleID, value)
+    end
+
+  function StateHandler:MoveStateUp()
+    local before, after = self:GetNeighbors()
+    if before then
+      self:SwapOrder(before, self:GetName())
+      ApplyStates(self.bar)
+    end
+  end
+
+  function StateHandler:MoveStateDown()
+    local before, after = self:GetNeighbors()
+    if after then
+      self:SwapOrder(self:GetName(), after)
+      ApplyStates(self.bar)
+    end
+  end
+
+  function StateHandler:GetAnchorDisabled()
+    return not GetProperty(self.bar, self:GetName(), "anchorEnable")
+  end
+
+  function StateHandler:GetAnchorFrames(info)
+    self._anchorframes = self._anchorframes or { }
+    table.wipe(self._anchorframes)
+
+    table.insert(self._anchorframes, "UIParent")
+    for name, bar in ReAction:IterateBars() do
+      table.insert(self._anchorframes, bar:GetFrame():GetName())
+    end
+    return self._anchorframes
+  end
+
+  function StateHandler:GetAnchorFrame(info)
+    local value = self:GetProp(info)
+    for k,v in pairs(self._anchorframes) do
+      if v == value then
+        return k
+      end
+    end
+  end
+
+  function StateHandler:SetAnchorFrame(info, value)
+    local f = _G[self._anchorframes[value]]
+    if f then
+      SetFrameRef(self.bar:GetFrame(), "anchor-"..self:GetName(), f)
+      self:SetProp(info, f:GetName())
+    end
+  end
+
+  function StateHandler:SetAnchorPointProp(info, value)
+    self:SetProp(info, value ~= "NONE" and value or nil)
+  end
+
+  function StateHandler:GetAnchorPointProp(info)
+    return self:GetProp(info) or "NONE"
+  end
+
+  function StateHandler:GetScale(info)
+    return self:GetProp(info) or 1.0
+  end
+
+  function StateHandler:GetScaleDisabled()
+    return not GetProperty(self.bar, self:GetName(), "enableScale")
+  end
+
+  function StateHandler:GetAlpha(info)
+    return self:GetProp(info) or 1.0
+  end
+
+  function StateHandler:GetAlphaDisabled()
+    return not GetProperty(self.bar, self:GetName(), "enableAlpha")
+  end
+
+  function StateHandler:SetType(info, value)
+    self:SetRuleField("type", value)
+    self:FixAll()
+    ApplyStates(self.bar)
+  end
+
+  function StateHandler:GetType()
+    return self:GetRuleField("type")
+  end
+
+  function StateHandler:GetClearAllDisabled()
+    local t = self:GetRuleField("type")
+    return not( t == "any" or t == "all" or t == "custom")
+  end
+
+  function StateHandler:ClearAllConditions()
+    local t = self:GetRuleField("type")
+    if t == "custom" then
+      self:SetRuleField("custom","")
+    elseif t == "any" or t == "all" then
+      self:SetRuleField("values", {})
+    end
+    ApplyStates(self.bar)
+  end
+
+  function StateHandler:GetConditionsDisabled()
+    local t = self:GetRuleField("type")
+    return not( t == "any" or t == "all")
+  end
+
+  function StateHandler:SetCondition(info, key, value)
+    self:SetRuleField(ruleMap[key], value or nil, "values")
+    if value then
+      self:FixAll(ruleMap[key])
+    end
+    ApplyStates(self.bar)
+  end
+
+  function StateHandler:GetCondition(info, key)
+    return self:GetRuleField("values", ruleMap[key]) or false
+  end
+
+  function StateHandler:GetCustomDisabled()
+    return self:GetRuleField("type") ~= "custom"
+  end
+
+  function StateHandler:SetCustomRule(info, value)
+    self:SetRuleField("custom",value)
+    ApplyStates(self.bar)
+  end
+
+  function StateHandler:GetCustomRule()
+    return self:GetRuleField("custom") or ""
+  end
+
+  function StateHandler:ValidateCustomRule(info, value)
+    local s = value:gsub("%s","") -- remove all spaces
+    -- unfortunately %b and captures don't support the '+' notation, or this would be considerably simpler
+    repeat
+      if s == "" then
+        return true
+      end
+      local c, r = s:match("(%b[])(.*)")
+      if c == nil and s and #s > 0 then
+        return format(L["Invalid custom rule '%s': each clause must appear within [brackets]"],value or "")
+      end
+      s = r
+    until c == nil
+    return true
+  end
+
+  function StateHandler:GetKeybindDisabled()
+    return self:GetRuleField("type") ~= "keybind"
+  end
+
+  function StateHandler:GetKeybind()
+    return self:GetRuleField("keybind")
+  end
+
+  function StateHandler:SetKeybind(info, value)
+    if value and #value == 0 then
+      value = nil
+    end
+    self:SetRuleField("keybind",value)
+    ApplyStates(self.bar)
+  end
+
+  local function CreateStateOptions(bar, name)
+    local opts = { 
+      type = "group",
+      name = name,
+      childGroups = "tab",
+      args = stateOptions
+    }
+
+    opts.handler = StateHandler:New(bar,opts)
+
+    return opts
+  end
+
+  function module:GetBarOptions(bar)
+    local private = { }
+    local states = tbuild(module.db.profile.bars, bar:GetName(), "states")
+    local options = {
+      name = L["Dynamic State"],
+      type = "group",
+      order = -1,
+      childGroups = "tree",
+      disabled = InCombatLockdown,
+      args = {
+        __desc__ = {
+          name = L["States are evaluated in the order they are listed"],
+          order = 1,
+          type = "description",
+        },
+        __new__ = {
+          name = L["New State..."],
+          order = 2,
+          type = "group",
+          args = {
+            name = {
+              name = L["State Name"],
+              desc = L["Set a name for the new state"],
+              order = 1,
+              type = "input",
+              get = function() return private.newstatename or "" end,
+              set = function(info,value) private.newstatename = value end,
+              pattern = "^%w*$",
+              usage = L["State names must be alphanumeric without spaces"],
+            },
+            create = {
+              name = L["Create State"],
+              order = 2,
+              type = "execute",
+              func = function ()
+                  local name = private.newstatename
+                  if states[name] then
+                    ReAction:UserError(format(L["State named '%s' already exists"],name))
+                  else
+                    -- TODO: select default state options and pass as final argument
+                    states[name] = { }
+                    optionMap[bar].args[name] = CreateStateOptions(bar,name)
+                    ReAction:ShowEditor(bar, moduleID, name)
+                    private.newstatename = ""
+                  end
+                end,
+              disabled = function()
+                  local name = private.newstatename or ""
+                  return #name == 0 or name:find("%W")
+                end,
+            }
+          }
+        }
+      }
+    }
+    for name, config in pairs(states) do
+      options.args[name] = CreateStateOptions(bar,name)
+    end
+    optionMap[bar] = options
+    return options
+  end
+end
+
+-- Module API --
+
+-- Pass in a property field-name, an implementation secure snippet, a static options table, and an 
+-- optional options handler method-table
+--
+-- The options table is static, i.e. not bar-specific and should only reference handler method
+-- strings (either existing ones or those added via optHandler). The existing options are ordered
+-- 90-99. Order #1 is reserved for the heading.
+--
+-- The contents of optHandler, if provided, will be added to the existing StateHandler options metatable.
+-- See above, for existing API. In particular see the properties set up in the New method: self.bar,
+-- self.states, and self:GetName(), and the generic property handlers self:GetProp() and self:SetProp().
+--
+function module:RegisterStateProperty( field, snippetHandler, options, optHandler )
+  RegisterProperty(field, snippetHandler)
+  RegisterPropertyOptions(field, options, optHandler)
+end
+
+function module:UnregisterStateProperty( field )
+  UnregisterProperty(field)
+  UnregisterPropertyOptions(field)
+end
+
+
+-- Export methods to Bar class --
+
+function ReAction.Bar:GetState()
+  return GetManagedEnvironment(self:GetFrame()).state
+end
+
+ReAction.Bar.GetStateProperty = GetProperty
+ReAction.Bar.SetStateProperty = SetProperty
--- a/modules/modules.xml	Wed Jan 07 00:57:02 2009 +0000
+++ b/modules/modules.xml	Thu Jan 08 00:57:27 2009 +0000
@@ -2,17 +2,15 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://www.blizzard.com/wow/ui/..\FrameXML\UI.xsd">
 
-<!-- config/layout modules -->
-<Include file="ReAction_ConfigUI\ReAction_ConfigUI.xml"/>
-<Include file="ReAction_HideBlizzard\ReAction_HideBlizzard.xml"/>
-
-
-<!-- button modules -->
-<Include file="ReAction_Action\ReAction_Action.xml"/>
-<Include file="ReAction_PetAction\ReAction_PetAction.xml"/>
-
-
-<!-- other modules -->
-<Include file="ReAction_ButtonFacade\ReAction_ButtonFacade.xml"/>
+<Script file="bindings.lua"/>
+<Script file="Bar.lua"/>
+<Script file="Overlay.lua"/>
+<Script file="Config.lua"/>
+<Script file="State.lua"/>
+<Script file="LDB.lua"/>
+<Script file="LBF.lua"/>
+<Script file="HideBlizzard.lua"/>
+<Script file="Action.lua"/>
+<Script file="PetAction.lua"/>
 
 </Ui>
\ No newline at end of file