if not modules then modules = { } end modules ['node-fin'] = {
    version   = 1.001,
    comment   = "companion to node-fin.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files",
}

-- this module is being reconstructed
-- local functions, only slightly slower

local next, type, format = next, type, string.format

local attributes, nodes, node = attributes, nodes, node

local copy_node       = node.copy
local find_tail       = node.slide

local nodecodes       = nodes.nodecodes
local whatcodes       = nodes.whatcodes

local glyph_code      = nodecodes.glyph
local disc_code       = nodecodes.disc
local glue_code       = nodecodes.glue
local rule_code       = nodecodes.rule
local whatsit_code    = nodecodes.whatsit
local hlist_code      = nodecodes.hlist
local vlist_code      = nodecodes.vlist

local pdfliteral_code = whatcodes.pdfliteral

local states          = attributes.states
local numbers         = attributes.numbers
local a_trigger       = attributes.private('trigger')
local triggering      = false

local starttiming     = statistics.starttiming
local stoptiming      = statistics.stoptiming
local loadstripped    = utilities.lua.loadstripped
local unsetvalue      = attributes.unsetvalue

-- these two will be like trackers

function states.enabletriggering()
    triggering = true
end
function states.disabletriggering()
    triggering = false
end

-- the following code is no longer needed due to the new backend
-- but we keep it around for a while as an example
--
-- states.collected = states.collected or { }
--
-- storage.register("states/collected", states.collected, "states.collected")
--
-- local collected = states.collected
--
-- function states.collect(str)
--     collected[#collected+1] = str
-- end
--
-- function states.flush()
--     if #collected > 0 then
--         for i=1,#collected do
--             context(collected[i]) -- we're in context mode anyway
--         end
--         collected = { }
--         states.collected = collected
--     end
-- end
--
-- function states.check()
--     logs.report("states",concat(collected,"\n"))
-- end

-- we used to do the main processor loop here and call processor for each node
-- but eventually this was too much a slow down (1 sec on 23 for 120 pages mk)
-- so that we moved looping to the processor itself; this may lead to a bit of
-- duplicate code once that we have more state handlers

-- local function process_attribute(head,plugin) -- head,attribute,enabled,initializer,resolver,processor,finalizer
--     local namespace = plugin.namespace
--     if namespace.enabled ~= false then -- this test will go away
--         starttiming(attributes) -- in principle we could delegate this to the main caller
--         local done, used, ok = false, nil, false
--         local attribute = namespace.attribute or numbers[plugin.name] -- todo: plugin.attribute
--         local processor = plugin.processor
--         if processor then
--             local initializer = plugin.initializer
--             local resolver    = plugin.resolver
--             local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
--             if initializer then
--                 initializer(namespace,attribute,head)
--             end
--             head, ok = processor(namespace,attribute,head,inheritance)
--             if ok then
--                 local finalizer = plugin.finalizer
--                 if finalizer then
--                     head, ok, used = finalizer(namespace,attribute,head)
--                     if used then
--                         local flusher = plugin.flusher
--                         if flusher then
--                             head = flusher(namespace,attribute,head,used)
--                         end
--                     end
--                 end
--                 done = true
--             end
--         end
--         stoptiming(attributes)
--         return head, done
--     else
--         return head, false
--     end
-- end
--
-- function nodes.installattributehandler(plugin) -- we need to avoid this nested function
--     return function(head)
--         return process_attribute(head,plugin)
--     end
-- end

-- An experiment: lean and mean functions. It is not really faster but
-- with upcoming functionality it might make a difference, e.g. features
-- like 'casing' and 'italics' can be called a lot so there it makes sense.

nodes.plugindata = nil

local template = [[
local plugin = nodes.plugindata
local starttiming = statistics.starttiming
local stoptiming = statistics.stoptiming
local namespace = plugin.namespace
local attribute = namespace.attribute or attributes.numbers[plugin.name]
local processor = plugin.processor
local initializer = plugin.initializer
local resolver = plugin.resolver
local finalizer = plugin.finalizer
local flusher = plugin.flusher
if not processor then
    return function(head)
        return head, false
    end
elseif initializer or finalizer or resolver then
    return function(head)
        starttiming(attributes)
        local done, used, ok = false, nil, false
        local inheritance = (resolver and resolver()) or nil -- -0x7FFFFFFF -- we can best use nil and skip !
        if initializer then
            initializer(namespace,attribute,head)
        end
        head, ok = processor(namespace,attribute,head,inheritance)
        if ok then
            if finalizer then
                head, ok, used = finalizer(namespace,attribute,head)
                if used and flusher then
                    head = flusher(namespace,attribute,head,used)
                end
            end
            done = true
        end
        stoptiming(attributes)
        return head, done
    end
else
    return function(head)
        starttiming(attributes)
        local head, done = processor(namespace,attribute,head)
        stoptiming(attributes)
        return head, done
    end
end
nodes.plugindata = nil
]]

function nodes.installattributehandler(plugin)
    nodes.plugindata = plugin
    return loadstripped(template)()
end

-- the injectors

local insert_node_before = node.insert_before
local insert_node_after  = node.insert_after

local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger
local current, current_selector, done = 0, 0, false -- nb, stack has a local current !
local nsbegin, nsend

function states.initialize(namespace,attribute,head)
    nsdata           = namespace.data
    nsnone           = namespace.none
    nsforced         = namespace.forced
    nsselector       = namespace.selector
    nslistwise       = namespace.listwise
    nstrigger        = triggering and namespace.triggering and a_trigger
    current          = 0
    current_selector = 0
    done             = false -- todo: done cleanup
    nsstep           = namespace.resolve_step
    if nsstep then
        nsbegin      = namespace.resolve_begin
        nsend        = namespace.resolve_end
        nspush       = namespace.push
        nspop        = namespace.pop
    end
end

function states.finalize(namespace,attribute,head) -- is this one ok?
    if current > 0 and nsnone then
        local id = head.id
        if id == hlist_code or id == vlist_code then
            local list = head.list
            if list then
                head.list = insert_node_before(list,list,copy_node(nsnone))
            end
        else
            head = insert_node_before(head,head,copy_node(nsnone))
        end
        return head, true, true
    end
    return head, false, false
end

-- disc nodes can be ignored
-- we need to deal with literals too (reset as well as oval)
-- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then

-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--     local stack, done = head, false
--     while stack do
--         local id = stack.id
--         if id == glyph_code or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--             local c = stack[attribute]
--             if c then
--                 if default and c == inheritance then
--                     if current ~= default then
--                         head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                         current = default
--                         done = true
--                     end
--                 elseif current ~= c then
--                     head = insert_node_before(head,stack,copy_node(nsdata[c]))
--                     current = c
--                     done = true
--                 end
--                 -- here ? compare selective
--                 if id == glue_code then --leader
--                     -- same as *list
--                     local content = stack.leader
--                     if content then
--                         local savedcurrent = current
--                         local ci = content.id
--                         if ci == hlist_code or ci == vlist_code then
--                             -- else we reset inside a box unneeded, okay, the downside is
--                             -- that we trigger color in each repeated box, so there is room
--                             -- for improvement here
--                             current = 0
--                         end
--                         local ok = false
--                         if nstrigger and stack[nstrigger] then
--                             local outer = stack[attribute]
--                             if outer ~= inheritance then
--                                 stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--                             else
--                                 stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--                             end
--                         else
--                             stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--                         end
--                         current = savedcurrent
--                         done = done or ok
--                     end
--                 end
--             elseif default and inheritance then
--                 if current ~= default then
--                     head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                     current = default
--                     done = true
--                 end
--             elseif current > 0 then
--                 head = insert_node_before(head,stack,copy_node(nsnone))
--                 current = 0
--                 done = true
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then
--                 local ok = false
--                 if nstrigger and stack[nstrigger] then
--                     local outer = stack[attribute]
--                     if outer ~= inheritance then
--                         stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--                     else
--                         stack.list, ok = process(namespace,attribute,content,inheritance,default)
--                     end
--                 else
--                     stack.list, ok = process(namespace,attribute,content,inheritance,default)
--                 end
--                 done = done or ok
--             end
--         end
--         stack = stack.next
--     end
--     return head, done
-- end

-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--     local stack, done = head, false

--     local function check()
--         local c = stack[attribute]
--         if c then
--             if default and c == inheritance then
--                 if current ~= default then
--                     head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                     current = default
--                     done = true
--                 end
--             elseif current ~= c then
--                 head = insert_node_before(head,stack,copy_node(nsdata[c]))
--                 current = c
--                 done = true
--             end
--         elseif default and inheritance then
--             if current ~= default then
--                 head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                 current = default
--                 done = true
--             end
--         elseif current > 0 then
--             head = insert_node_before(head,stack,copy_node(nsnone))
--             current = 0
--             done = true
--         end
--         return c
--     end

--     local function nested(content)
--         if nstrigger and stack[nstrigger] then
--             local outer = stack[attribute]
--             if outer ~= inheritance then
--                 return process(namespace,attribute,content,inheritance,outer)
--             else
--                 return process(namespace,attribute,content,inheritance,default)
--             end
--         else
--             return process(namespace,attribute,content,inheritance,default)
--         end
--     end

--     while stack do
--         local id = stack.id
--         if id == glyph_code then
--             check()
--         elseif id == glue_code then
--             local content = stack.leader
--             if content and check() then
--                 local savedcurrent = current
--                 local ci = content.id
--                 if ci == hlist_code or ci == vlist_code then
--                     -- else we reset inside a box unneeded, okay, the downside is
--                     -- that we trigger color in each repeated box, so there is room
--                     -- for improvement here
--                     current = 0
--                 end

--                 local ok = false
--                 stack.leader, ok = nested(content)
--                 done = done or ok

--                 current = savedcurrent
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then

--                 local ok = false
--                 stack.list, ok = nested(content)
--                 done = done or ok

--             end
--         elseif id == rule_code then
--             if stack.width ~= 0 then
--                 check()
--             end
--         end
--         stack = stack.next
--     end
--     return head, done
-- end

-- local function process(namespace,attribute,head,inheritance,default) -- one attribute
--     local stack, done = head, false
--     while stack do
--         local id = stack.id
--         if id == glyph_code then
--             -- begin of check
--             local c = stack[attribute]
--             if c then
--                 if default and c == inheritance then
--                     if current ~= default then
--                         head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                         current = default
--                         done = true
--                     end
--                 elseif current ~= c then
--                     head = insert_node_before(head,stack,copy_node(nsdata[c]))
--                     current = c
--                     done = true
--                 end
--             elseif default and inheritance then
--                 if current ~= default then
--                     head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                     current = default
--                     done = true
--                 end
--             elseif current > 0 then
--                 head = insert_node_before(head,stack,copy_node(nsnone))
--                 current = 0
--                 done = true
--             end
--             -- end of check
--         elseif id == glue_code then
--             local content = stack.leader
--             if content then
--                 -- begin of check
--                 local c = stack[attribute]
--                 if c then
--                     if default and c == inheritance then
--                         if current ~= default then
--                             head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                             current = default
--                             done = true
--                         end
--                     elseif current ~= c then
--                         head = insert_node_before(head,stack,copy_node(nsdata[c]))
--                         current = c
--                         done = true
--                     end
--                     -- begin special to this check
--                     local savedcurrent = current
--                     local ci = content.id
--                     if ci == hlist_code or ci == vlist_code then
--                         -- else we reset inside a box unneeded, okay, the downside is
--                         -- that we trigger color in each repeated box, so there is room
--                         -- for improvement here
--                         current = 0
--                     end
--                     -- begin nested --
--                     local ok = false
--                     if nstrigger and stack[nstrigger] then
--                         local outer = stack[attribute]
--                         if outer ~= inheritance then
--                             stack.leader, ok = process(namespace,attribute,content,inheritance,outer)
--                         else
--                             stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--                         end
--                     else
--                         stack.leader, ok = process(namespace,attribute,content,inheritance,default)
--                     end
--                     -- end nested --
--                     done = done or ok
--                     current = savedcurrent
--                     -- end special to this check
--                 elseif default and inheritance then
--                     if current ~= default then
--                         head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                         current = default
--                         done = true
--                     end
--                 elseif current > 0 then
--                     head = insert_node_before(head,stack,copy_node(nsnone))
--                     current = 0
--                     done = true
--                 end
--                 -- end of check
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then
--                 -- begin nested --
--                 local ok
--                 if nstrigger and stack[nstrigger] then
--                     local outer = stack[attribute]
--                     if outer ~= inheritance then
--                         stack.list, ok = process(namespace,attribute,content,inheritance,outer)
--                     else
--                         stack.list, ok = process(namespace,attribute,content,inheritance,default)
--                     end
--                 else
--                     stack.list, ok = process(namespace,attribute,content,inheritance,default)
--                 end
--                 -- end nested --
--                 done = done or ok
--             end
--         elseif id == rule_code then
--             if stack.width ~= 0 then
--                 -- begin of check
--                 local c = stack[attribute]
--                 if c then
--                     if default and c == inheritance then
--                         if current ~= default then
--                             head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                             current = default
--                             done = true
--                         end
--                     elseif current ~= c then
--                         head = insert_node_before(head,stack,copy_node(nsdata[c]))
--                         current = c
--                         done = true
--                     end
--                 elseif default and inheritance then
--                     if current ~= default then
--                         head = insert_node_before(head,stack,copy_node(nsdata[default]))
--                         current = default
--                         done = true
--                     end
--                 elseif current > 0 then
--                     head = insert_node_before(head,stack,copy_node(nsnone))
--                     current = 0
--                     done = true
--                 end
--                 -- end of check
--             end
--         end
--         stack = stack.next
--     end
--     return head, done
-- end

local function process(namespace,attribute,head,inheritance,default) -- one attribute
    local stack  = head
    local done   = false
    local check  = false
    local leader = nil
    while stack do
        local id = stack.id
        if id == glyph_code then
            check = true
        elseif id == glue_code then
            leader = stack.leader
            if leader then
                check = true
            end
        elseif id == hlist_code or id == vlist_code then
            local content = stack.list
            if content then
                -- begin nested --
                local ok
                if nstrigger and stack[nstrigger] then
                    local outer = stack[attribute]
                    if outer ~= inheritance then
                        stack.list, ok = process(namespace,attribute,content,inheritance,outer)
                    else
                        stack.list, ok = process(namespace,attribute,content,inheritance,default)
                    end
                else
                    stack.list, ok = process(namespace,attribute,content,inheritance,default)
                end
                -- end nested --
                done = done or ok
            end
        elseif id == rule_code then
            check = stack.width ~= 0
        end
        -- much faster this way than using a check() and nested() function
        if check then
            local c = stack[attribute]
            if c then
                if default and c == inheritance then
                    if current ~= default then
                        head = insert_node_before(head,stack,copy_node(nsdata[default]))
                        current = default
                        done = true
                    end
                elseif current ~= c then
                    head = insert_node_before(head,stack,copy_node(nsdata[c]))
                    current = c
                    done = true
                end
                if leader then
                    local savedcurrent = current
                    local ci = leader.id
                    if ci == hlist_code or ci == vlist_code then
                        -- else we reset inside a box unneeded, okay, the downside is
                        -- that we trigger color in each repeated box, so there is room
                        -- for improvement here
                        current = 0
                    end
                    -- begin nested --
                    local ok = false
                    if nstrigger and stack[nstrigger] then
                        local outer = stack[attribute]
                        if outer ~= inheritance then
                            stack.leader, ok = process(namespace,attribute,leader,inheritance,outer)
                        else
                            stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
                        end
                    else
                        stack.leader, ok = process(namespace,attribute,leader,inheritance,default)
                    end
                    -- end nested --
                    done = done or ok
                    current = savedcurrent
                    leader = false
                end
            elseif default and inheritance then
                if current ~= default then
                    head = insert_node_before(head,stack,copy_node(nsdata[default]))
                    current = default
                    done = true
                end
            elseif current > 0 then
                head = insert_node_before(head,stack,copy_node(nsnone))
                current = 0
                done = true
            end
            check = false
        end
        stack = stack.next
    end
    return head, done
end

states.process = process

-- we can force a selector, e.g. document wide color spaces, saves a little
-- watch out, we need to check both the selector state (like colorspace) and
-- the main state (like color), otherwise we get into troubles when a selector
-- state changes while the main state stays the same (like two glyphs following
-- each other with the same color but different color spaces e.g. \showcolor)

-- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
--     local stack, done = head, false
--     while stack do
--         local id = stack.id
--         -- we need to deal with literals too (reset as well as oval)
--         -- if id == glyph_code or (id == whatsit_code and stack.subtype == pdfliteral_code) or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--         if id == glyph_code -- or id == disc_code
--                 or (id == rule_code and stack.width ~= 0) or (id == glue_code and stack.leader) then -- or disc_code
--             local c = stack[attribute]
--             if c then
--                 if default and c == inheritance then
--                     if current ~= default then
--                         local data = nsdata[default]
--                         head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                         current = default
--                         done = true
--                     end
--                 else
--                     local s = stack[nsselector]
--                     if current ~= c or current_selector ~= s then
--                         local data = nsdata[c]
--                         head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                         current = c
--                         current_selector = s
--                         done = true
--                     end
--                 end
--             elseif default and inheritance then
--                 if current ~= default then
--                     local data = nsdata[default]
--                     head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                     current = default
--                     done = true
--                 end
--             elseif current > 0 then
--                 head = insert_node_before(head,stack,copy_node(nsnone))
--                 current, current_selector, done = 0, 0, true
--             end
--             if id == glue_code then -- leader
--                 -- same as *list
--                 local content = stack.leader
--                 if content then
--                     local savedcurrent = current
--                     local ci = content.id
--                     if ci == hlist_code or ci == vlist_code then
--                         -- else we reset inside a box unneeded, okay, the downside is
--                         -- that we trigger color in each repeated box, so there is room
--                         -- for improvement here
--                         current = 0
--                     end
--                     local ok = false
--                     if nstrigger and stack[nstrigger] then
--                         local outer = stack[attribute]
--                         if outer ~= inheritance then
--                             stack.leader, ok = selective(namespace,attribute,content,inheritance,outer)
--                         else
--                             stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--                         end
--                     else
--                         stack.leader, ok = selective(namespace,attribute,content,inheritance,default)
--                     end
--                     current = savedcurrent
--                     done = done or ok
--                 end
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then
--                 local ok = false
--                 if nstrigger and stack[nstrigger] then
--                     local outer = stack[attribute]
--                     if outer ~= inheritance then
--                         stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
--                     else
--                         stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--                     end
--                 else
--                     stack.list, ok = selective(namespace,attribute,content,inheritance,default)
--                 end
--                 done = done or ok
--             end
--         end
--         stack = stack.next
--     end
--     return head, done
-- end

-- local function selective(namespace,attribute,head,inheritance,default) -- two attributes
--     local stack, done = head, false

--     local function check()
--         local c = stack[attribute]
--         if c then
--             if default and c == inheritance then
--                 if current ~= default then
--                     local data = nsdata[default]
--                     head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                     current = default
--                     done = true
--                 end
--             else
--                 local s = stack[nsselector]
--                 if current ~= c or current_selector ~= s then
--                     local data = nsdata[c]
--                     head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                     current = c
--                     current_selector = s
--                     done = true
--                 end
--             end
--         elseif default and inheritance then
--             if current ~= default then
--                 local data = nsdata[default]
--                 head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
--                 current = default
--                 done = true
--             end
--         elseif current > 0 then
--             head = insert_node_before(head,stack,copy_node(nsnone))
--             current, current_selector, done = 0, 0, true
--         end
--         return c
--     end

--     local function nested(content)
--         if nstrigger and stack[nstrigger] then
--             local outer = stack[attribute]
--             if outer ~= inheritance then
--                 return selective(namespace,attribute,content,inheritance,outer)
--             else
--                 return selective(namespace,attribute,content,inheritance,default)
--             end
--         else
--             return selective(namespace,attribute,content,inheritance,default)
--         end
--     end

--     while stack do
--         local id = stack.id
--         if id == glyph_code then
--             check()
--         elseif id == glue_code then
--             local content = stack.leader
--             if content and check() then
--              -- local savedcurrent = current
--              -- local ci = content.id
--              -- if ci == hlist_code or ci == vlist_code then
--              --     -- else we reset inside a box unneeded, okay, the downside is
--              --     -- that we trigger color in each repeated box, so there is room
--              --     -- for improvement here
--              --     current = 0
--              -- end

--                 local ok = false
--                 stack.leader, ok = nested(content)
--                 done = done or ok

--              -- current = savedcurrent
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then

--                 local ok = false
--                 stack.list, ok = nested(content)
--                 done = done or ok

--             end
--         elseif id == rule_code then
--             if stack.width ~= 0 then
--                 check()
--             end
--         end
--         stack = stack.next
--     end
--     return head, done
-- end

local function selective(namespace,attribute,head,inheritance,default) -- two attributes
    local stack  = head
    local done   = false
    local check  = false
    local leader = nil
    while stack do
        local id = stack.id
        if id == glyph_code then
            check = true
        elseif id == glue_code then
            leader = stack.leader
            if leader then
                check = true
            end
        elseif id == hlist_code or id == vlist_code then
            local content = stack.list
            if content then
                local ok = false
                -- begin nested
                if nstrigger and stack[nstrigger] then
                    local outer = stack[attribute]
                    if outer ~= inheritance then
                        stack.list, ok = selective(namespace,attribute,content,inheritance,outer)
                    else
                        stack.list, ok = selective(namespace,attribute,content,inheritance,default)
                    end
                else
                    stack.list, ok = selective(namespace,attribute,content,inheritance,default)
                end
                -- end nested
                done = done or ok
            end
        elseif id == rule_code then
            check = stack.width ~= 0
        end

        if check then
            local c = stack[attribute]
            if c then
                if default and c == inheritance then
                    if current ~= default then
                        local data = nsdata[default]
                        head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
                        current = default
                        done = true
                    end
                else
                    local s = stack[nsselector]
                    if current ~= c or current_selector ~= s then
                        local data = nsdata[c]
                        head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
                        current = c
                        current_selector = s
                        done = true
                    end
                end
                if leader then
                    local ok = false
                    -- begin nested
                    if nstrigger and stack[nstrigger] then
                        local outer = stack[attribute]
                        if outer ~= inheritance then
                            stack.leader, ok = selective(namespace,attribute,leader,inheritance,outer)
                        else
                            stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
                        end
                    else
                        stack.leader, ok = selective(namespace,attribute,leader,inheritance,default)
                    end
                    -- end nested
                    done = done or ok
                    leader = false
                end
            elseif default and inheritance then
                if current ~= default then
                    local data = nsdata[default]
                    head = insert_node_before(head,stack,copy_node(data[nsforced or stack[nsselector] or nsselector]))
                    current = default
                    done = true
                end
            elseif current > 0 then
                head = insert_node_before(head,stack,copy_node(nsnone))
                current, current_selector, done = 0, 0, true
            end
            check = false
        end

        stack = stack.next
    end
    return head, done
end

states.selective = selective

-- Ideally the next one should be merged with the previous but keeping it separate is
-- safer. We deal with two situations: efficient boxwise (layoutareas) and mixed layers
-- (as used in the stepper). In the stepper we cannot use the box branch as it involves
-- paragraph lines and then gets mixed up. A messy business (esp since we want to be
-- efficient).
--
-- Todo: make a better stacker. Keep track (in attribute) about nesting level. Not
-- entirely trivial and a generic solution is nicer (compares to the exporter).

-- local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--     local stack, done = head, false
--     local current, depth = default or 0, 0
--
--     local function check()
--         local a = stack[attribute]
--         if a then
--             if current ~= a then
--                 head = insert_node_before(head,stack,copy_node(nsdata[a]))
--                 depth = depth + 1
--                 current, done = a, true
--             end
--         elseif default > 0 then
--             --
--         elseif current > 0 then
--             head = insert_node_before(head,stack,copy_node(nsnone))
--             depth = depth - 1
--             current, done = 0, true
--         end
--         return a
--     end
--
--     while stack do
--         local id = stack.id
--         if id == glyph_code then
--             check()
--         elseif id == glue_code then
--             local content = stack.leader
--             if content and check() then
--                 local ok = false
--                 stack.leader, ok = stacked(namespace,attribute,content,current)
--                 done = done or ok
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = stack.list
--             if content then
--              -- the problem is that broken lines gets the attribute which can be a later one
--                 if nslistwise then
--                     local a = stack[attribute]
--                     if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
--                         local p = current
--                         current, done = a, true
--                         head = insert_node_before(head,stack,copy_node(nsdata[a]))
--                         stack.list = stacked(namespace,attribute,content,current)
--                         head, stack = insert_node_after(head,stack,copy_node(nsnone))
--                         current = p
--                     else
--                         local ok = false
--                         stack.list, ok = stacked(namespace,attribute,content,current)
--                         done = done or ok
--                     end
--                 else
--                     local ok = false
--                     stack.list, ok = stacked(namespace,attribute,content,current)
--                     done = done or ok
--                 end
--             end
--         elseif id == rule_code then
--             if stack.width ~= 0 then
--                 check()
--             end
--         end
--         stack = stack.next
--     end
--     while depth > 0 do
--         head = insert_node_after(head,stack,copy_node(nsnone))
--         depth = depth - 1
--     end
--     return head, done
-- end

local function stacked(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
    local stack   = head
    local done    = false
    local current = default or 0
    local depth   = 0
    local check   = false
    local leader  = false
    while stack do
        local id = stack.id
        if id == glyph_code then
            check = true
        elseif id == glue_code then
            leader = stack.leader
            if leader then
                check = true
            end
        elseif id == hlist_code or id == vlist_code then
            local content = stack.list
            if content then
             -- the problem is that broken lines gets the attribute which can be a later one
                if nslistwise then
                    local a = stack[attribute]
                    if a and current ~= a and nslistwise[a] then -- viewerlayer / needs checking, see below
                        local p = current
                        current, done = a, true
                        head = insert_node_before(head,stack,copy_node(nsdata[a]))
                        stack.list = stacked(namespace,attribute,content,current)
                        head, stack = insert_node_after(head,stack,copy_node(nsnone))
                        current = p
                    else
                        local ok = false
                        stack.list, ok = stacked(namespace,attribute,content,current)
                        done = done or ok
                    end
                else
                    local ok = false
                    stack.list, ok = stacked(namespace,attribute,content,current)
                    done = done or ok
                end
            end
        elseif id == rule_code then
            check = stack.width ~= 0
        end

        if check then
            local a = stack[attribute]
            if a then
                if current ~= a then
                    head = insert_node_before(head,stack,copy_node(nsdata[a]))
                    depth = depth + 1
                    current, done = a, true
                end
                if leader then
                    local ok = false
                    stack.leader, ok = stacked(namespace,attribute,content,current)
                    done = done or ok
                    leader = false
                end
            elseif default > 0 then
                --
            elseif current > 0 then
                head = insert_node_before(head,stack,copy_node(nsnone))
                depth = depth - 1
                current, done = 0, true
            end
            check = false
        end

        stack = stack.next
    end
    while depth > 0 do
        head = insert_node_after(head,stack,copy_node(nsnone))
        depth = depth - 1
    end
    return head, done
end

states.stacked = stacked

-- experimental

-- local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
--     nsbegin()
--     local current, previous, done, okay = head, head, false, false
--     local attrib = default or unsetvalue
--
--     local function check()
--         local a = current[attribute] or unsetvalue
--         if a ~= attrib then
--             local n = nsstep(a)
--             if n then
--              -- !!!! TEST CODE !!!!
--              -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
--                 head = insert_node_before(head,current,n) -- a
--             end
--             attrib, done, okay = a, true, true
--         end
--         return a
--     end
--
--     while current do
--         local id = current.id
--         if id == glyph_code then
--             check()
--         elseif id == glue_code then
--             local content = current.leader
--             if content and check() then
--                 -- tricky as a leader has to be a list so we cannot inject before
--                 local _, ok = stacker(namespace,attribute,content,attrib)
--                 done = done or ok
--             end
--         elseif id == hlist_code or id == vlist_code then
--             local content = current.list
--             if not content then
--                 -- skip
--             elseif nslistwise then
--                 local a = current[attribute]
--                 if a and attrib ~= a and nslistwise[a] then -- viewerlayer
--                     done = true
--                     head = insert_node_before(head,current,copy_node(nsdata[a]))
--                     current.list = stacker(namespace,attribute,content,a)
--                     head, current = insert_node_after(head,current,copy_node(nsnone))
--                 else
--                     local ok = false
--                     current.list, ok = stacker(namespace,attribute,content,attrib)
--                     done = done or ok
--                 end
--             else
--                 local ok = false
--                 current.list, ok = stacker(namespace,attribute,content,default)
--                 done = done or ok
--             end
--         elseif id == rule_code then
--             if current.width ~= 0 then
--                 check()
--             end
--         end
--         previous = current
--         current = current.next
--     end
--     if okay then
--         local n = nsend()
--         if n then
--              -- !!!! TEST CODE !!!!
--          -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
--             head = insert_node_after(head,previous,n)
--         end
--     end
--     return head, done
-- end

local function stacker(namespace,attribute,head,default) -- no triggering, no inheritance, but list-wise
    nsbegin()
    local current  = head
    local previous = head
    local done     = false
    local okay     = false
    local attrib   = default or unsetvalue
    local check    = false
    local leader   = false
    while current do
        local id = current.id
        if id == glyph_code then
            check = true
        elseif id == glue_code then
            leader = current.leader
            if leader then
                check = true
            end
        elseif id == hlist_code or id == vlist_code then
            local content = current.list
            if not content then
                -- skip
            elseif nslistwise then
                local a = current[attribute]
                if a and attrib ~= a and nslistwise[a] then -- viewerlayer
                    done = true
                    head = insert_node_before(head,current,copy_node(nsdata[a]))
                    current.list = stacker(namespace,attribute,content,a)
                    head, current = insert_node_after(head,current,copy_node(nsnone))
                else
                    local ok = false
                    current.list, ok = stacker(namespace,attribute,content,attrib)
                    done = done or ok
                end
            else
                local ok = false
                current.list, ok = stacker(namespace,attribute,content,default)
                done = done or ok
            end
        elseif id == rule_code then
            check = current.width ~= 0
        end

        if check then
            local a = current[attribute] or unsetvalue
            if a ~= attrib then
                local n = nsstep(a)
                if n then
                 -- !!!! TEST CODE !!!!
                 -- head = insert_node_before(head,current,copy_node(nsdata[tonumber(n)])) -- a
                    head = insert_node_before(head,current,n) -- a
                end
                attrib, done, okay = a, true, true
                if leader then
                    -- tricky as a leader has to be a list so we cannot inject before
                    local _, ok = stacker(namespace,attribute,leader,attrib)
                    done = done or ok
                    leader = false
                end
            end
            check = false
        end

        previous = current
        current = current.next
    end
    if okay then
        local n = nsend()
        if n then
             -- !!!! TEST CODE !!!!
         -- head = insert_node_after(head,previous,copy_node(nsdata[tostring(n)]))
            head = insert_node_after(head,previous,n)
        end
    end
    return head, done
end

states.stacker = stacker

-- -- --

statistics.register("attribute processing time", function()
    return statistics.elapsedseconds(attributes,"front- and backend")
end)
