vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
---------------------------------------------------------------------------
-- Iriel's Virtual Frame Driver - Embedded library
--
-- Written by Iriel <iriel@vigilance-committee.org>
---------------------------------------------------------------------------
-- IMPORTANT: Do not **EDIT** and distribute without changing MAJOR_VERSION

IVF_Warnings = false;

local lib = {};

-- Return the library's current version
function lib:GetLibraryVersion()
        -- You MUST update the major version whenever you make an incompatible
        -- change
        local MAJOR_VERSION = "KarlPrototype-2-dev";
        -- You MUST update the minor version whenever you make a compatible
        -- change (And check LibActivate is still valid!)
        local MINOR_VERSION = 618;

        return MAJOR_VERSION, MINOR_VERSION;
end

-- Activate a new instance of this library
function lib:LibActivate(stub, oldLib, oldList)
        local maj, min = self:GetLibraryVersion();

        -- For now there's no migration
        self:_Initialize();

        -- nil return makes stub do object copy
end

---------------------------------------------------------------------------
-- CODE NOTES
--
-- The single letter variable P is used for the 'merged properties' table
-- for a frame, that is the composite table created by stacking the various
-- inherited properties under one another.

---------------------------------------------------------------------------
-- PROPERTY HANDLERS
--
-- Property handlers are invoked by the instantiation engine to configure
-- newly created frames. They're all called with the same set of parameters:
--
-- object -- The frame/region object that's being configured.
-- key   -- The name of the property that triggered the handler.
-- value  -- The value of the property that triggered the handler.
-- props  -- The 'processing properties' table, which has the following
--                        entries:
--
--        props.engine    -- The library instance that's handling instantiation
--        props.spec             -- The specification object for the current object
--        props.name             -- The name of the current object
--        props.object    -- The current object
--        props.properties -- The merged properties for the current object
--        props:Error(msg) -- An error reporting function
--        props.object    -- A table of any objects created as properties (such
--                                                               as subtextures or fonts), indexed by their property
--                                                               name.
--
-- If a property handler is registered for multiple keys, it is called
-- ONCE if any of the keys are defined on the object (or if the registration
-- entry has runAlways = true set on it it's called ONCE regardless).
--
-- Handlers are applied in the order they're registered in the _InitProperties
-- method.

local function PH_SetValue(object, key, value, props)
        local methodName = "Set" .. key;
        local method = object[methodName];
        if (not method) then
                error("Missing method '" .. methodName .. "'");
        end
        method(object, value);
end

local function Create_PH_MethodCaller(methodName)
        return function(object, key, value)
                                 local method = object[methodName];
                                 if (not method) then
                                         error("Missing method '" .. methodName .. "'");
                                 end
                                 method(object, value);
                         end
end

local function PH_SetSpecialTexture(object, key, value, props)
        local tex = props.objects[key];
        if (not tex) then
                error("Missing texture");
        end
        PH_SetValue(object, key, tex, props);
end

local function PH_SetSpecialFrame(object, key, value, props)
        local frame = props.objects[key];
        if (not frame) then
                error("Missing frame");
        end
        PH_SetValue(object, key, frame, props);
end

local function Create_PH_SpecialFontSetter(methodName)
        return function(object, key, value, props)
                                 local font = props.objects[key];
                                 PH_SetValue(object, methodName, font, props);
                         end
end

local function Create_PH_HTMLFontSetter(whichFont)
        return function(object, key, value, props)
                                 local font = props.objects[key];
                                 if (not font) then
                                         error("Missing Font");
                                 end
                                 object:SetFont(whichFont, font);
                         end
end

local function PH_SetAnchors(object, key, value, props)
        if (props.spec.type == "Font") then
                return;
        end
        local parent = props.parent;
        local P = props.properties;
        local anchors = P.Anchors;
        local allPoints = P.SetAllPoints;
        if (anchors) then
                -- If we have explicit anchors, process them all
                object:ClearAllPoints();
                local parname = parent and parent:GetName();
                for _,a in ipairs(anchors) do
                        local p,rel,rp,x,y = a[1], a[2], a[3], a[4], a[5];
                        if (rel) then
                                rel = string.gsub(rel, "%$parent", parname or '');
                        end
                        if (not rel or rel == '') then rel = parent; end
                        object:SetPoint(p, rel, rp, x, y);
                end
        elseif ((allPoints) or (allPoints == nil)) then
                -- Otherwise if we've explicitly been asked to set all points,
                -- or the flag has not been set at all, then try and set all
                -- points to the parent.
                if (parent) then
                        object:SetAllPoints(parent);
                else
                        -- TODO: Use 'screen' parent code once 1.11 launches
                end
        end
end

local function PH_SetFont(object, key, value, props)
        local P = props.properties;
        local fontPath = P.Font;
        local height = P.FontHeight or 0;
        local outline = P.Outline;
        local monochrome = P.Monochrome;
        local flags = nil;
        if (outline == "NORMAL") then
                flags = "OUTLINE";
        elseif (outline == "THICK") then
                flags = "OUTLINE,THICKOUTLINE";
        end
        if (monochrome) then
                if (flags) then
                        flags = flags .. ",MONOCHROME";
                else
                        flags = "MONOCHROME";
                end
        end
        object:SetFont(fontPath, height, flags);
end

local function PH_SetShadow(object, key, value, props)
        local C = value.color;
        local O = value.offset;
        if (value.color) then
                object:SetShadowColor(C[1], C[2], C[3], C[4]);
        end
        if (value.offset) then
                object:SetShadowOffset(C[1], C[2]);
        end
end

---------------------------------------------------------------------------
-- LIBRARY METHODS
--
-- These are the actual library methods. Method names beginning with an
-- underscore are intended for internal use only.
--
local VFMETHODS = {};
setmetatable(lib, { __index = VFMETHODS } );

function VFMETHODS:_Initialize()
        self.specs = {};
        self.scriptNames = {};
        self.propertyHandlers = {};
        self.propertyNames = {};
        self.workTables = {};

        self:_InitProperties();
end

function VFMETHODS:_GetWorkTable()
        local tbl = next(self.workTables);
        if (not tbl) then
                return {};
        end
        self.workTables[tbl] = nil;
        return tbl;
end

function VFMETHODS:_ReleaseWorkTable(tbl)
        for k,v in pairs(tbl) do
                tbl[k] = nil;
        end
        table.setn(tbl, 0);
        self.workTables[tbl] = true;
end

-- Setup function for registering a new property handler with the
-- generation code. See PROPERTY HANDLERS section above.
--
-- func - The property handling function to use.
-- ...  - One or more property names that the function handles.
--
-- Returns the property handler entry so that it can have flags set on
-- it if necessary.
function VFMETHODS:_AddPropertyHandler(func, ...)
        local entry = {
                names = arg;
                func = func;
        };
        table.insert(self.propertyHandlers, entry);
        for _, n in ipairs(arg) do
                local curHandler = self.propertyNames[n];
                if (curHandler) then
                        self:Warning("Duplicate property handler for '" .. n .. "'");
                else
                        self.propertyNames[n] = entry;
                end
        end
        return entry;
end

function VFMETHODS:_InitProperties()
        -- Block special properties
        self.propertyNames["type"]        = true;
        self.propertyNames["name"]        = true;
        self.propertyNames["inherits"] = true;
        self.propertyNames["Parent"]    = true;

        ---------------------------------------------------------------------------
        -- Group 0, basic properties
        self:_AddPropertyHandler(PH_SetValue, "Alpha");
        self:_AddPropertyHandler(PH_SetValue, "DrawLayer");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                if (value) then
                                                                                        object:Hide();
                                                                                else
                                                                                        object:Show();
                                                                                end
                                                                        end, "Hidden");
        self:_AddPropertyHandler(PH_SetValue, "ID");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                if (value == "PARENT") then
                                                                                        local par = props.parent;
                                                                                        if (par) then
                                                                                                value = par:GetFrameStrata();
                                                                                        else
                                                                                                value = "MEDIUM";
                                                                                        end
                                                                                end
                                                                                object:SetFrameStrata(value);
                                                                        end, "FrameStrata");
        self:_AddPropertyHandler(PH_SetValue, "Movable");
        self:_AddPropertyHandler(PH_SetValue, "Resizable");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                if (value[1]) then
                                                                                        object:SetWidth(value[1]);
                                                                                end
                                                                                if (value[2]) then
                                                                                        object:SetHeight(value[2]);
                                                                                end
                                                                        end, "Size");
        self:_AddPropertyHandler(PH_SetValue, "FrameLevel");
        self:_AddPropertyHandler(PH_SetAnchors,
                                                                        "SetAllPoints", "Anchors").runAlways = true;
        self:_AddPropertyHandler(PH_SetValue, "TopLevel");

        ---------------------------------------------------------------------------
        -- Group 1, sub-objects
        self:_AddPropertyHandler(PH_SetValue, "Texture");
        self:_AddPropertyHandler(PH_SetFont,
                                                                         "Font", "FontHeight", "Outline", "Monochrome");
        self:_AddPropertyHandler(Create_PH_SpecialFontSetter("FontObject")
                                                                        , "FontString");
        self:_AddPropertyHandler(Create_PH_SpecialFontSetter("TextFontObject"),
                                                                        "NormalText");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "NormalTexture");
        self:_AddPropertyHandler(PH_SetValue, "Backdrop");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "CheckedTexture");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "DisabledCheckedTexture");
        self:_AddPropertyHandler(Create_PH_SpecialFontSetter("DisabledFontObject"),
                                                                        "DisabledText");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "DisabledTexture");
        self:_AddPropertyHandler(Create_PH_HTMLFontSetter("H1"),
                                                                         "FontStringHeader1");
        self:_AddPropertyHandler(Create_PH_HTMLFontSetter("H2"),
                                                                         "FontStringHeader2");
        self:_AddPropertyHandler(Create_PH_HTMLFontSetter("H3"),
                                                                         "FontStringHeader3");
        self:_AddPropertyHandler(Create_PH_SpecialFontSetter("HighlightFontObject"),
                                                                        "HighlightText");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "HighlightTexture");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "PushedTexture");
        self:_AddPropertyHandler(PH_SetSpecialFrame, "ScrollChild");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                -- *** UGLY ***
                                                                                local tex = props.objects[key];
                                                                                object:SetStatusBarTexture(tex:GetTexture());
                                                                        end, "StatusBarTexture");
        self:_AddPropertyHandler(PH_SetSpecialTexture, "ThumbTexture");
        --self:_AddPropertyHandler(PH_SetValue, "ArrowModel"); -- Missing (Minimap)
        --self:_AddPropertyHandler(PH_SetValue, "PlayerModel"); -- Missing
        self:_AddPropertyHandler(PH_SetValue, "Model");

        ---------------------------------------------------------------------------
        -- Group 2 - Colors
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                object:SetHighlightTextColor(value[1], value[2], value[3], value[4]);
                                                                        end, "HighlightColor");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                object:SetTexture(value[1], value[2], value[3], value[4]);
                                                                        end, "Color");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                object:SetStatusBarColor(value[1], value[2], value[3], value[4]);
                                                                        end, "BarColor");

        ---------------------------------------------------------------------------
        -- Group 3 - Configuration

        self:_AddPropertyHandler(Create_PH_MethodCaller("SetBlendMode"),
                                                                         "AlphaMode");
        --self:_AddPropertyHandler(PH_SetValue, "AutoFocus"); -- 1.11
        --self:_AddPropertyHandler(PH_SetValue, "BlinkSpeed"); -- Missing (EditBox)
        self:_AddPropertyHandler(Create_PH_MethodCaller("SetTimeVisible"),
                                                                         "DisplayDuration");
        self:_AddPropertyHandler(PH_SetValue, "FogFar");
        self:_AddPropertyHandler(PH_SetValue, "FogNear");
        self:_AddPropertyHandler(PH_SetValue, "HistoryLines");
        self:_AddPropertyHandler(PH_SetValue, "HyperlinkFormat");
        -- IgnoreArrows -- GetAltArrowKeyMode ?
        -- InsertMode  -- 1.11
        self:_AddPropertyHandler(PH_SetValue, "JustifyH");
        self:_AddPropertyHandler(PH_SetValue, "JustifyV");
        self:_AddPropertyHandler(PH_SetValue, "MaxBytes");
        self:_AddPropertyHandler(PH_SetValue, "MaxLetters");
        self:_AddPropertyHandler(PH_SetValue, "MaxLines");
        self:_AddPropertyHandler(function (object, key, value, props)
                                                                                local P = props.properties;
                                                                                local min, max = P.MinValue, P.MaxValue;
                                                                                if ((not min) or (not max)) then
                                                                                        local omin, omax = object:GetMinMaxValues();
                                                                                        min = min or omin;
                                                                                        max = max or omax;
                                                                                end
                                                                                object:SetMinMaxValues(min, max);
                                                                        end, "MinValue", "MaxValue");
        self:_AddPropertyHandler(PH_SetValue, "ModelScale");
        self:_AddPropertyHandler(PH_SetValue, "NonSpaceWrap");
        self:_AddPropertyHandler(PH_SetValue, "Orientation");
        self:_AddPropertyHandler(PH_SetValue, "Spacing");
        self:_AddPropertyHandler(PH_SetValue, "ValueStep");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                local max = value.max;
                                                                                if (max) then
                                                                                        object:SetMaxResize(max[1], max[2]);
                                                                                end
                                                                                local min = value.min;
                                                                                if (min) then
                                                                                        object:SetMinResize(max[1], max[2]);
                                                                                end
                                                                        end, "ResizeBounds");
        self:_AddPropertyHandler(PH_SetShadow, "Shadow");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                object:SetTexCoord(value.left, value.right,
                                                                                                                                 value.top, value.bottom);
                                                                        end, "TexCoords");
        self:_AddPropertyHandler(
                                                                        function(object, key, value, props)
                                                                                object:SetTextInsets(value.left, value.right,
                                                                                                                                        value.top, value.bottom);
                                                                        end, "TextInsets");

        ---------------------------------------------------------------------------
        -- Group 4 - State

        self:_AddPropertyHandler(PH_SetValue, "Checked");
        self:_AddPropertyHandler(PH_SetValue, "Text");
        self:_AddPropertyHandler(PH_SetValue, "Value");

        -- Others

        -- File -- Missing (SimpleHTML)
        -- MultiLine -- 1.11
        -- Numeric - 1.11
        -- Password -- Missing

        -- ColorValueTexture -- Missing
        -- ColorValueThumbTexture -- Missing
        -- ColorWheelTexture -- Missing
        -- ColorWheelThumbTexture -- Missing
        -- FogColor -- todo
        -- Gradient -- todo
        -- HitRectInsets -- missing
        -- PushedTexOffset -- missing
        -- TitleRegion -- missing
end

function VFMETHODS:_Prepare(spec, path)
        local myName;
        if (spec.name) then
                myName = spec.name .. "<" .. (spec.type or '?') .. ">";
        else
                myName = "<" .. (spec.type or '?') .. ">";
        end

        if (not path) then
                path = myName;
        else
                path = path .. ":" .. myName;
        end

        for k,v in pairs(spec) do
                if ((type(v) == "function") and string.find(k,"^On")) then
                        self.scriptNames[k] = true;
                elseif (type(v) == "table" and v.type) then
                        self:_Prepare(v, path .. "[" .. k .. "]");
                elseif (not self.propertyNames[k]) then
                        self:Warning("Unsupported property '" .. k .. "' used by "
                                                         .. path);
                end
        end
end

function VFMETHODS:Register(name, spec)
        -- self:Debug("Registering '" .. name .. "'");
        self:_Prepare(spec);
        self.specs[name] = spec;
end

function VFMETHODS:Debug(msg)
        ChatFrame2:AddMessage("[VirtualFrames] " .. msg);
end

function VFMETHODS:Warning(msg)
        if (IVF_Warnings) then
                DEFAULT_CHAT_FRAME:AddMessage("[VirtualFrames] WARNING: " .. msg);
        end
end

function VFMETHODS:Error(msg)
        DEFAULT_CHAT_FRAME:AddMessage("[VirtualFrames] ERROR: " .. msg);
        message(msg);
end

function VFMETHODS:Instantiate(template, name, parent, properties, noOnLoad)
        local spec = self.specs[template];
        if (not spec) then
                self:Error("No template for '" .. template .. "' defined");
                return;
        end

        if (type(parent) == "string") then
                local parentObj = getglobal(parent);
                if (not parentObj) then
                        self:Error("Parent '" .. parentObj .. "' not found");
                        return;
                end
                parent = parentObj;
        end

        if (properties) then
                for key, value in pairs(properties) do
                        if ((type(value) == "function") and string.find(key, "^On")) then
                                self.scriptNames[key] = true;
                        elseif (not self.propertyNames[key]) then
                                self:Warning("Unsupported property '" .. key .. "' in input.");
                        end
                end
        end

        local context = self:_GetWorkTable();
        context.specs = self:_GetWorkTable();

        local obj, objname = self:_ObjectCreate(context, spec, name,
                                                                                                                 parent, properties);
        local P = self:_ObjectComplete(context,
                                                                                         spec, obj, objname, properties);

        self:_ObjectActivate(context, spec, obj, properties, noOnLoad);

        self:_ReleaseWorkTable(context.specs);
        self:_ReleaseWorkTable(context);
        context = nil;
        if (properties) then
                local meta = getmetatable(properties);
                if (meta) then
                        meta.__index = nil;
                end
        end
        return obj, objname;
end

function VFMETHODS:_LayerProperties(spec, P)
        if (P) then
                local meta = getmetatable(P);
                if (not meta) then
                        meta = {};
                        setmetatable(P, meta);
                end
                if (not meta.__index) then
                        meta.__index = spec;
                end
        else
                P = spec;
        end

        return P;
end

-- CREATE: This handles creating an object and building the merged
--                      property structure for the remainder of the process.
--
--        context - The context structure for the instantiation process
--        spec   - The spec structure defining the object to create
--        name   - The requested object name (nil to use spec)
--        parent  - The parent object (nil to use spec)
--        props - Source properties for the processing run
--
-- Returns the created object, its name, and its parent object
function VFMETHODS:_ObjectCreate(context, spec, name, parent, props)
        --self:Debug("  Create "..spec.type.." (" .. (spec.name or "") ..") "
        --..(name or '?'));

        local P = self:_LayerProperties(spec, props);

        local object, inheritFont;
        local objType = rawget(spec, "type");
        local inherits = rawget(spec, "inherits");
        if (inherits) then
                if ((objType == "FontString") or (objType == "Font")) then
                        font = getglobal(inherits);
                        if (font and font.IsObjectType and font:IsObjectType("Font")) then
                                inheritFont = font;
                        end
                end
                if (not inheritFont) then
                        -- self:Debug("  Inherits from " .. spec.inherits);
                        local ispec = self.specs[inherits];
                        if (not ispec) then
                                self:Error("No template for '" .. inherits .. "' defined");
                        elseif (rawget(ispec, "type") ~= objType) then
                                self:Error("Type mismatch with template '" .. inherits .. "'");
                        else
                                local pmeta = getmetatable(spec);
                                if (not pmeta) then
                                        pmeta = {};
                                        setmetatable(spec, pmeta);
                                end
                                pmeta.__index = ispec;
                                object = self:_ObjectCreate(context, ispec, name, parent, P);
                        end
                end
        end

        if (not object) then
                if (not parent) then
                        local PParent = P.Parent;
                        if (PParent) then
                                parent = getglobal(PParent);
                                if (not parent) then
                                        self:Warning("Unable to find parent frame '" .. PParent .. "'");
                                end
                        end
                end

                if (objType == "Texture") then
                        object = parent:CreateTexture(name);
                elseif (objType == "FontString") then
                        object = parent:CreateFontString(name);
                        if (inheritFont) then
                                object:SetFontObject(inheritFont);
                        end
                elseif (objType == "Font") then
                        object = CreateFont(name or " unnamed font ");
                        if (inheritFont) then
                                object:SetFontObject(inheritFont);
                        end
                else
                        object = CreateFrame(objType, name, parent);
                end

                context.specs[object] = spec;
        end

        return object, name, parent;
end

local function PW_ErrorMethod(self, error)
        local context = self.name;
        local spec = self.spec;
        if (not context and spec.name) then
                context = "[" .. spec.name .. "]";
        end
        if (not context and spec.type) then
                context = "[" .. spec.type .. "]";
        end
        if (not context) then
                context = "?";
        end
        if (self.curName) then
                context = context .. ": " .. self.curName;
        end
        self.engine:Error(context .. ": " .. error);
end

-- COMPLETE: This handles creating child objects, then applying properties
--                        to the object and child objects.
--
--        context - The context structure for the instantiation process
--        spec   - The spec structure defining the object to create
--        object  - The object to be completed
--        name   - The requested object name (nil to use spec)
--        props - Source properties for the processing run
--        noApply - If true, suppress application of properties on the object
--
-- Returns the merged properties of the object.
--
-- Completion works as follows:
--
--      1) If this spec inherits from another one, then call _ObjectComplete
--              for the same object but with the inherited spec, props == the
--              merged properties for the object, and and noApply = true, continue
--              when that returns.
--      2) _ObjectCreate all child objects declared by THIS spec (not including
--              special property objects) with this as parent.
--      3) If noApply is false, then _ObjectCreate all property objects.
--      4) _ObjectComplete all child objects declared by THIS spec.
--      5) if noApply is false, then _ObjectComplete all property objects.
--      6) if noApply is false, then _ObjectActivate all property objects.
--      7) if noApply is false, then apply all relevant property handlers.
--      8) _ObjectActivate all child objects declared by THIS spec.
--      9) return merged properties.
function VFMETHODS:_ObjectComplete(context, spec, object, name, props, noApply)
        --self:Debug("  Complete "..spec.type.." (" .. (spec.name or "") ..") "
        --..(name or '?'));

        local P = self:_LayerProperties(spec, props);
        local baseSpec = context.specs[object];
        local objType = rawget(spec, "type");
        local inherited = false;
        if ((baseSpec ~= spec) and (objType ~= 'Font')) then
                local inherits = rawget(spec, "inherits");
                local ispec = self.specs[inherits];
                if (ispec) then
                        inherited = true;
                        P = self:_ObjectComplete(context, ispec, object, name, P, true);
                end
        end

        -- Create sub objects
        local childObjects = self:_GetWorkTable();
        local propObjects = self:_GetWorkTable();

        for k, v in ipairs(spec) do
                local cname = v.name;
                if (cname) then
                        cname = string.gsub(cname, "%$parent", name or '');
                end
                childObjects[k] = self:_ObjectCreate(context, v, cname, object);
        end

        if (not noApply) then
                for n in pairs(self.propertyNames) do
                        local v = P[n];
                        if ((type(v) == "table") and (v.type)) then
                                local cname = v.name;
                                if (cname) then
                                        cname = string.gsub(cname, "%$parent", name or '');
                                end
                                propObjects[n] = self:_ObjectCreate(context, v, cname, object);
                        end
                end
        end

        -- Now complete child objects
        for k, spec in ipairs(spec) do
                local obj = childObjects[k];
                self:_ObjectComplete(context, spec, obj, obj:GetName());
        end

        if (not noApply) then
                for k, obj in pairs(propObjects) do
                        local spec = P[k];
                        self:_ObjectComplete(context, spec, obj, obj:GetName());
                end

                -- Populate this frame's properties
                for k, obj in pairs(propObjects) do
                        local spec = P[k];
                        self:_ObjectActivate(context, spec, obj);
                end

                local propWorker = self:_GetWorkTable();
                propWorker.engine = self;
                propWorker.spec = spec;
                propWorker.name = name;
                propWorker.object = object;
                propWorker.properties = P;
                propWorker.parent = object.GetParent and object:GetParent();
                propWorker.Error = PW_ErrorMethod;
                propWorker.objects = propObjects;

                for _,p in ipairs(self.propertyHandlers) do
                        for _,n in ipairs(p.names) do
                                local v = P[n];
                                if ((v ~= nil) or (p.runAlways)) then
                                        propWorker.curName = n;
                                        local ok, err = pcall(p.func,
                                                                                                 object, n, v,
                                                                                                 propWorker, p);
                                        if (not ok) then
                                                propWorker:Error("Failed: " .. err);
                                        end
                                        break;
                                end
                        end
                end

                self:_ReleaseWorkTable(propWorker);
                propWorker = nil;
        end

        -- Activate child objects
        for k, spec in ipairs(spec) do
                local obj = childObjects[k];
                self:_ObjectActivate(context, spec, obj);
        end

        self:_ReleaseWorkTable(childObjects);
        self:_ReleaseWorkTable(propObjects);
        regionObjects, childObjects, propObjects = nil, nil, nil;

        return P;
end

-- ACTIVATE: Apply scripts and invoke OnLoad if necessary.
--
--        context  - The context structure for the instantiation process
--        spec    - The spec structure defining the object to create
--        object        - The object to activate
--        props  - Source properties for the processing run
--        noOnLoad - If true, don't call OnLoad method if it exists
function VFMETHODS:_ObjectActivate(context, spec, object, props, noOnLoad)
        --self:Debug("  Activate "..spec.type.." (" .. (spec.name or "") ..") "
        --..(object:GetName() or '?'));
        local P = self:_LayerProperties(spec, props);

        local HS = object.HasScript;
        local OnLoad;
        for scriptName in pairs(self.scriptNames) do
                local script = P[scriptName];
                if (type(script) == "function") then
                        if (HS and HS(object, scriptName)) then
                                object:SetScript(scriptName, script);
                                if (scriptName == "OnLoad") then
                                        OnLoad = script;
                                end
                        else
                                self:Error("Attempted to set " .. k .. " script on a "
                                                          .. spec.type);
                        end
                end
        end

        if (OnLoad and (not noOnLoad)) then
                -- self:Debug("Firing OnLoad " .. tostring(object:GetName() or object));
                local oldthis = this;
                this = object;
                local ok,err = pcall(OnLoad);
                if (not ok) then
                        self:Error("OnLoad failed for " .. spec.type );
                        self:Error(tostring(err));
                end
                this = oldthis;
        end
end

-- Register this instance with the stub
IrielVirtualFrames:Register(lib);
lib = nil; -- Let GC clean it up later