From 14e4b284db6588fa128d92cdac6f96685da1719f Mon Sep 17 00:00:00 2001
From: Hans Hagen <pragma@wxs.nl>
Date: Tue, 20 May 2014 09:53:00 +0200
Subject: beta 2014.05.20 09:53

---
 tex/context/base/anch-bar.mkiv                     |   2 +-
 tex/context/base/cont-new.mkiv                     |   2 +-
 tex/context/base/context-version.pdf               | Bin 4283 -> 4284 bytes
 tex/context/base/context.mkiv                      |   2 +-
 tex/context/base/core-env.mkiv                     |  40 +++-
 tex/context/base/lpdf-ano.lua                      |   2 +-
 tex/context/base/publ-dat.lua                      |   1 +
 tex/context/base/publ-fnd.lua                      | 173 ++++++++++++++
 tex/context/base/publ-imp-cite.mkiv                |   7 +
 tex/context/base/publ-ini.lua                      | 261 ++++++++++++++++-----
 tex/context/base/publ-ini.mkiv                     |  95 +++++---
 tex/context/base/spac-ver.lua                      | 121 +++++++---
 tex/context/base/status-files.pdf                  | Bin 24609 -> 24594 bytes
 tex/context/base/status-lua.pdf                    | Bin 244006 -> 244074 bytes
 tex/context/base/strc-ref.mkvi                     |   5 +-
 tex/context/base/strc-sec.mkiv                     |   9 +-
 tex/context/base/task-ini.lua                      |   1 -
 tex/context/base/trac-ctx.mkiv                     |   1 -
 tex/context/base/trac-vis.mkiv                     |  13 +-
 tex/context/base/x-mathml-html.mkiv                |  40 ++++
 tex/generic/context/luatex/luatex-fonts-merged.lua |   2 +-
 21 files changed, 624 insertions(+), 153 deletions(-)
 create mode 100644 tex/context/base/publ-fnd.lua
 create mode 100644 tex/context/base/x-mathml-html.mkiv

diff --git a/tex/context/base/anch-bar.mkiv b/tex/context/base/anch-bar.mkiv
index 501507b3b..844655155 100644
--- a/tex/context/base/anch-bar.mkiv
+++ b/tex/context/base/anch-bar.mkiv
@@ -19,7 +19,7 @@
 %D functionality from \type {core-pos}.
 %D
 %D \starttyping
-%D \definesidebar[whow][rulecolor=green,distance=]
+%D \definesidebar[whow][rulecolor=green,distance=0pt]
 %D
 %D \input tufte \par
 %D \startsidebar
diff --git a/tex/context/base/cont-new.mkiv b/tex/context/base/cont-new.mkiv
index e20a7df91..e47436dde 100644
--- a/tex/context/base/cont-new.mkiv
+++ b/tex/context/base/cont-new.mkiv
@@ -11,7 +11,7 @@
 %C therefore copyrighted by \PRAGMA. See mreadme.pdf for
 %C details.
 
-\newcontextversion{2014.05.18 15:57}
+\newcontextversion{2014.05.20 09:53}
 
 %D This file is loaded at runtime, thereby providing an excellent place for
 %D hacks, patches, extensions and new features.
diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf
index acc0c1f36..da217cd7b 100644
Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ
diff --git a/tex/context/base/context.mkiv b/tex/context/base/context.mkiv
index abf7d5c14..40f7c56cb 100644
--- a/tex/context/base/context.mkiv
+++ b/tex/context/base/context.mkiv
@@ -28,7 +28,7 @@
 %D up and the dependencies are more consistent.
 
 \edef\contextformat {\jobname}
-\edef\contextversion{2014.05.18 15:57}
+\edef\contextversion{2014.05.20 09:53}
 \edef\contextkind   {beta}
 
 %D For those who want to use this:
diff --git a/tex/context/base/core-env.mkiv b/tex/context/base/core-env.mkiv
index ca134e230..75b65418f 100644
--- a/tex/context/base/core-env.mkiv
+++ b/tex/context/base/core-env.mkiv
@@ -331,15 +331,35 @@
 
 \letvalue{\??setup:\letterpercent}\gobbleoneargument
 
+% \def\syst_setups#1% the grid option will be extended to other main modes
+%   {\csname\??setup
+%      \ifgridsnapping
+%        \ifcsname\??setup\v!grid:#1\endcsname\v!grid:#1\else\ifcsname\??setup:#1\endcsname:#1\else:\letterpercent\fi\fi
+%      \else
+%                                                            \ifcsname\??setup:#1\endcsname:#1\else:\letterpercent\fi
+%      \fi
+%    \endcsname\empty} % takes one argument
+
 \def\syst_setups#1% the grid option will be extended to other main modes
   {\csname\??setup
      \ifgridsnapping
-       \ifcsname\??setup\v!grid:#1\endcsname\v!grid:#1\else\ifcsname\??setup:#1\endcsname:#1\else:\letterpercent\fi\fi
+       \ifcsname\??setup\v!grid:#1\endcsname\v!grid:#1\else:\ifcsname\??setup:#1\endcsname#1\else\letterpercent\fi\fi
      \else
-                                                           \ifcsname\??setup:#1\endcsname:#1\else:\letterpercent\fi
+                                                           :\ifcsname\??setup:#1\endcsname#1\else\letterpercent\fi
      \fi
    \endcsname\empty} % takes one argument
 
+% not faster but less tracing sometimes:
+%
+% \def\syst_setups% the grid option will be extended to other main modes
+%   {\csname\??setup\ifgridsnapping\expandafter\syst_setups_grid\else\expandafter\syst_setups_normal\fi}
+%
+% \def\syst_setups_grid#1%
+%   {\ifcsname\??setup\v!grid:#1\endcsname\v!grid:#1\else\ifcsname\??setup:#1\endcsname:#1\else:\letterpercent\fi\fi\endcsname\empty} % takes one argument
+%
+% \def\syst_setups_normal#1%
+%   {:\ifcsname\??setup:#1\endcsname#1\else\letterpercent\fi\endcsname\empty} % takes one argument
+
 % We can consider:
 %
 % \setvalue{\??setup->\v!auto}#1{\ctxcommand{autosetup("#1")}}
@@ -467,17 +487,11 @@
 
 % Is doglobal still relevant? Maybe always global? Or never? Anyway, it will become obsolete.
 
-\unexpanded\def\startluasetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_lua_a\syst_setups_start_lua_b}
-\unexpanded\def\startxmlsetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_xml_a\syst_setups_start_xml_b}
-\unexpanded\def\startrawsetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_raw_a\syst_setups_start_raw_b}
-\unexpanded\def\startlocalsetups{\begingroup\doifnextoptionalcselse\syst_setups_start_loc_a\syst_setups_start_loc_b}
-\unexpanded\def\startsetups     {\begingroup\doifnextoptionalcselse\syst_setups_start_tex_a\syst_setups_start_tex_b}
-
-\let\stopluasetups              \relax
-\let\stopxmlsetups              \relax
-\let\stoprawsetups              \relax
-\let\stoplocalsetups            \relax
-\let\stopsetups                 \relax
+\unexpanded\def\startluasetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_lua_a\syst_setups_start_lua_b} \let\stopluasetups              \relax
+\unexpanded\def\startxmlsetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_xml_a\syst_setups_start_xml_b} \let\stopxmlsetups              \relax
+\unexpanded\def\startrawsetups  {\begingroup\doifnextoptionalcselse\syst_setups_start_raw_a\syst_setups_start_raw_b} \let\stoprawsetups              \relax
+\unexpanded\def\startlocalsetups{\begingroup\doifnextoptionalcselse\syst_setups_start_loc_a\syst_setups_start_loc_b} \let\stoplocalsetups            \relax
+\unexpanded\def\startsetups     {\begingroup\doifnextoptionalcselse\syst_setups_start_tex_a\syst_setups_start_tex_b} \let\stopsetups                 \relax
 
 \def\syst_setups_start_lua_indeed#1#2#3\stopluasetups  {\endgroup\dodoglobal\expandafter\def\csname\??setup#1:#2\expandafter\endcsname\expandafter##\expandafter1\expandafter{#3}}
 \def\syst_setups_start_xml_indeed#1#2#3\stopxmlsetups  {\endgroup\dodoglobal\expandafter\def\csname\??setup#1:#2\expandafter\endcsname\expandafter##\expandafter1\expandafter{#3}}
diff --git a/tex/context/base/lpdf-ano.lua b/tex/context/base/lpdf-ano.lua
index 827c43ec6..ab3a74053 100644
--- a/tex/context/base/lpdf-ano.lua
+++ b/tex/context/base/lpdf-ano.lua
@@ -776,7 +776,7 @@ function specials.internal(var,actions) -- better resolve in strc-ref
     local v = i and references.internals[i]
     if not v then
         -- error
-        report_reference("no internal reference %a",i)
+        report_reference("no internal reference %a",i or "<unset>")
     else
         flaginternals[i] = true
         return pdflink(nil,i,v.references.realpage)
diff --git a/tex/context/base/publ-dat.lua b/tex/context/base/publ-dat.lua
index 5e255029a..5ad4829e0 100644
--- a/tex/context/base/publ-dat.lua
+++ b/tex/context/base/publ-dat.lua
@@ -117,6 +117,7 @@ function publications.new(name)
         nofentries = 0,
         shortcuts  = { },
         luadata    = { },
+        suffixes   = { },
         xmldata    = xmlconvert(xmlplaceholder),
      -- details    = { },
         nofbytes   = 0,
diff --git a/tex/context/base/publ-fnd.lua b/tex/context/base/publ-fnd.lua
new file mode 100644
index 000000000..77d1d4dab
--- /dev/null
+++ b/tex/context/base/publ-fnd.lua
@@ -0,0 +1,173 @@
+if not modules then modules = { } end modules ['publ-fnd'] = {
+    version   = 1.001,
+    comment   = "this module part of publication support",
+    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+    copyright = "PRAGMA ADE / ConTeXt Development Team",
+    license   = "see context related readme files"
+}
+
+local tonumber, next = tonumber, next
+local P, R, C, Cs, Carg = lpeg.P, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Carg
+local lpegmatch = lpeg.match
+local concat = table.concat
+local find = string.find
+
+local formatters = string.formatters
+local lowercase  = characters.lower
+
+local colon   = P(":")
+local dash    = P("-")
+local lparent = P("(")
+local rparent = P(")")
+local space   = lpeg.patterns.whitespace
+local valid   = 1 - colon - space - lparent - rparent
+local key     = C(valid^1)
+local key     = C(R("az","AZ")^1)
+local word    = Cs(lpeg.patterns.unquoted + valid^1)
+local number  = C(valid^1)
+
+----- f_string_key = formatters["  local s_%s = entry[%q]"]
+local f_string_key = formatters["  local s_%s = entry[%q] if s_%s then s_%s = lower(s_%s) end "]
+local f_number_key = formatters["  local n_%s = tonumber(entry[%q]) or 0"]
+local f_field_key  = formatters["  local f_%s = entry[%q] or ''"]
+
+----- f_string_match = formatters["(s_%s and find(lower(s_%s),%q))"]
+local f_string_match = formatters["(s_%s and find(s_%s,%q))"]
+local f_number_match = formatters["(n_%s and n_%s >= %s and n_%s <= %s)"]
+local f_field_match  = formatters["f_%s"]
+
+local match  = key * (colon/"") * word * Carg(1) / function(key,_,word,keys)
+ -- keys[key] = f_string_key(key,key)
+    keys[key] = f_string_key(key,key,key,key,key)
+    return f_string_match(key,key,lowercase(word))
+end
+
+local range  = key * (colon/"") * number * (dash/"") * number * Carg(1)  / function(key,_,first,_,last,keys)
+    keys[key] = f_number_key(key,key)
+    return f_number_match(key,key,tonumber(first) or 0,key,tonumber(last) or 0)
+end
+
+local field  = (P("field:")/"") * key * Carg(1) / function(_,key,keys)
+    keys[key] = f_field_key(key,key)
+    return f_field_match(key)
+end
+
+----- pattern = Cs((field + range + match + P(1))^1)
+----- b_match = P("match")/"" * lparent
+local b_match = lparent
+local e_match = rparent * P(-1)
+local pattern = Cs(b_match * ((field + range + match + P(1))-e_match)^1 * e_match)
+
+-- -- -- -- -- -- -- -- -- -- -- -- --
+-- -- -- -- -- -- -- -- -- -- -- -- --
+
+local tolower  = lpeg.patterns.tolower
+local lower    = string.lower
+
+local allascii = R("\000\127")^1 * P(-1)
+
+function characters.checkedlower(str)
+    return lpegmatch(allascii,str) and lower(str) or lpegmatch(tolower,str) or str
+end
+
+-- -- -- -- -- -- -- -- -- -- -- -- --
+-- -- -- -- -- -- -- -- -- -- -- -- --
+
+local f_template = string.formatters[ [[
+local find = string.find
+local lower = characters.checkedlower
+return function(entry)
+%s
+return %s and true or false
+end
+]] ]
+
+local function compile(expr,start)
+    local keys        = { }
+    local expression  = lpegmatch(pattern,expr,start,keys)
+ -- print("!!!!",expression)
+    local definitions = { }
+    for k, v in next, keys do
+        definitions[#definitions+1] = v
+    end
+    definitions = concat(definitions,"\n")
+    local code = f_template(definitions,expression)
+ -- print(code)
+    code = loadstring(code)
+    if type(code) == "function" then
+        code = code()
+        if type(code) == "function" then
+            return code
+        end
+    end
+    print("no valid expression",expression)
+    return false
+end
+
+local cache = { } -- todo: make weak, or just remember the last one (trial typesetting)
+
+local function finder(expression)
+    local b, e = find(expression,"^match")
+    if e then
+        local found  = cache[expression]
+        if found == nil then
+            found = compile(expression,e+1) or false
+            cache[expression] = found
+        end
+        return found
+    end
+end
+
+publications.finder = finder
+
+function publications.search(dataset,expression)
+    local find   = finder(expression)
+    local source = dataset.luadata
+    if find then
+        local target = { }
+        for k, v in next, source do
+            if find(v) then
+                target[k] = v
+            end
+        end
+        return target
+    else
+        return { source[expression] }
+    end
+end
+
+
+-- local dataset = publications.new()
+-- publications.load(dataset,"t:/manuals/hybrid/tugboat.bib")
+--
+-- local n = 500
+--
+-- local function test(dataset,str)
+--     local found
+--     local start = os.clock()
+--     for i=1,n do
+--         found = search(dataset,str)
+--     end
+--     local elapsed = os.clock() - start
+--     print(elapsed,elapsed/500,#table.keys(dataset.luadata),str)
+--     print(table.concat(table.sortedkeys(found)," "))
+--     return found
+-- end
+--
+-- local found = test(dataset,[[match(author:hagen)]])
+-- local found = test(dataset,[[match(author:hagen and author:hoekwater and year:1990-2010)]])
+-- local found = test(dataset,[[match(author:"Bogusław Jackowski")]])
+-- local found = test(dataset,[[match(author:"Bogusław Jackowski" and (tonumber(field:year) or 0) > 2000)]])
+-- local found = test(dataset,[[Hagen:TB19-3-304]])
+
+-- 1.328	0.002656	2710	author:hagen
+-- Berdnikov:TB21-2-129 Guenther:TB5-1-24 Hagen:TB17-1-54 Hagen:TB19-3-304 Hagen:TB19-3-311 Hagen:TB19-3-317 Hagen:TB22-1-58 Hagen:TB22-3-118 Hagen:TB22-3-136 Hagen:TB22-3-160 Hagen:TB23-1-49 Hagen:TB25-1-108 Hagen:TB25-1-48 Hagen:TB26-2-152
+
+-- 1.812	0.003624	2710	author:hagen and author:hoekwater and year:1990-2010
+-- Berdnikov:TB21-2-129
+
+-- 1.344	0.002688	2710	author:"Bogusław Jackowski"
+-- Berdnikov:TB21-2-129 Jackowski:TB16-4-388 Jackowski:TB19-3-267 Jackowski:TB19-3-272 Jackowski:TB20-2-104 Jackowski:TB24-1-64 Jackowski:TB24-3-575 Nowacki:TB19-3-242 Rycko:TB14-3-171
+
+-- 1.391	0.002782	2710	author:"Bogusław Jackowski" and (tonumber(field:year) or 0) > 2000
+-- Jackowski:TB24-1-64 Jackowski:TB24-3-575
diff --git a/tex/context/base/publ-imp-cite.mkiv b/tex/context/base/publ-imp-cite.mkiv
index 0f0c69fa7..17a93aa82 100644
--- a/tex/context/base/publ-imp-cite.mkiv
+++ b/tex/context/base/publ-imp-cite.mkiv
@@ -70,6 +70,7 @@
     \directsetup{\s!btx:\s!cite:concat}
     \ifconditional\btxinteractive
         \goto {
+            \btxcitereference
             \ifx\currentbtxsecond\empty
                 \currentbtxfirst
             \else
@@ -110,6 +111,7 @@
     \directsetup{\s!btx:\s!cite:concat}
     \ifconditional\btxinteractive
         \goto {
+            \btxcitereference
             \ifx\currentbtxsecond\empty
                 \currentbtxfirst
             \else
@@ -135,6 +137,7 @@
     \directsetup{\s!btx:\s!cite:concat}
     \ifconditional\btxinteractive
         \goto {
+            \btxcitereference
             \directsetup{\s!btx:\s!cite:render:\currentbtxcitevariant}
         } [
             \s!internal(\currentbtxinternal)
@@ -152,6 +155,10 @@
     \else
         \currentbtxciteauthor
     \fi
+    \ifx\currentbtxsecond\empty \else
+        \btxcitevariantparameter\v!inbetween
+        \currentbtxsecond
+    \fi
 \stopsetups
 
 \startsetups \s!btx:\s!cite:render:author
diff --git a/tex/context/base/publ-ini.lua b/tex/context/base/publ-ini.lua
index d713f552c..baac989cc 100644
--- a/tex/context/base/publ-ini.lua
+++ b/tex/context/base/publ-ini.lua
@@ -104,22 +104,23 @@ local settings_to_array = utilities.parsers.settings_to_array
 
 local context                     = context
 
-local ctx_btxlistparameter        = context.btxlistparameter
-local ctx_btxcitevariantparameter = context.btxcitevariantparameter
-local ctx_btxlistvariantparameter = context.btxlistvariantparameter
-local ctx_btxdomarkcitation       = context.btxdomarkcitation
 local ctx_setvalue                = context.setvalue
 local ctx_firstoftwoarguments     = context.firstoftwoarguments
 local ctx_secondoftwoarguments    = context.secondoftwoarguments
 local ctx_firstofoneargument      = context.firstofoneargument
 local ctx_gobbleoneargument       = context.gobbleoneargument
+----- ctx_directsetup             = context.directsetup
+
+local ctx_btxlistparameter        = context.btxlistparameter
+local ctx_btxcitevariantparameter = context.btxcitevariantparameter
+local ctx_btxlistvariantparameter = context.btxlistvariantparameter
+local ctx_btxdomarkcitation       = context.btxdomarkcitation
 local ctx_btxdirectlink           = context.btxdirectlink
 local ctx_btxhandlelistentry      = context.btxhandlelistentry
 local ctx_btxchecklistentry       = context.btxchecklistentry
 local ctx_btxchecklistcombi       = context.btxchecklistcombi
------ ctx_dodirectfullreference   = context.dodirectfullreference
-local ctx_btxsetreference         = context.btxsetreference
-local ctx_directsetup             = context.directsetup
+local ctx_btxsetcitereference     = context.btxsetcitereference
+local ctx_btxsetlistreference     = context.btxsetlistreference
 local ctx_btxmissing              = context.btxmissing
 
 local ctx_btxsettag               = context.btxsettag
@@ -389,42 +390,70 @@ end)
 --         end
 
 local reported = { }
+local finder   = publications.finder
 
 local function findallused(dataset,reference,block,section)
-    local tags  = settings_to_array(reference)
+local finder = publications.finder
+    local find  = finder and finder(reference)
+    local tags  = not find and settings_to_array(reference)
     local todo  = { }
     local okay  = { } -- only if mark
     local set   = usedentries[dataset]
     local valid = datasets[dataset].luadata
     if set then
-        for i=1,#tags do
-            local tag = tags[i]
-            if valid[tag] then
-                local entry = set[tag]
-                if entry then
-                    -- only once in a list
-                    if #entry == 1 then
-                        entry = entry[1]
-                    else
-                        -- find best match (todo)
-                        entry = entry[1] -- for now
-                    end
-                    okay[#okay+1] = entry
+        local function register(tag)
+            local entry = set[tag]
+            if entry then
+                -- only once in a list
+                if #entry == 1 then
+                    entry = entry[1]
+                else
+                    -- find best match (todo)
+                    entry = entry[1] -- for now
                 end
+                okay[#okay+1] = entry
                 todo[tag] = true
-            elseif not reported[tag] then
-                reported[tag] = true
-                report_cite("non-existent entry %a in %a",tag,dataset)
+            end
+        end
+        if find then
+            tags = { }
+            for tag, entry in next, valid do
+                local found = find(entry)
+                if found then
+                    register(tag)
+                    tags[#tags+1] = tag
+                end
+            end
+        else
+            for i=1,#tags do
+                local tag  = tags[i]
+                if valid[tag] then
+                    register(tag)
+                elseif not reported[tag] then
+                    reported[tag] = true
+                    report_cite("non-existent entry %a in %a",tag,dataset)
+                end
             end
         end
     else
-        for i=1,#tags do
-            local tag = tags[i]
-            if valid[tag] then
-                todo[tag] = true
-            elseif not reported[tag] then
-                reported[tag] = true
-                report_cite("non-existent entry %a in %a",tag,dataset)
+        if find then
+            tags = { }
+            for tag, entry in next, valid do
+                local found = find(entry)
+                if found then
+                    register(tag)
+                    tags[#tags+1] = tag
+                end
+            end
+        else
+            for i=1,#tags do
+                local tag = tags[i]
+                if valid[tag] then
+                    todo[tag] = true
+                elseif not reported[tag] then
+                    reported[tag] = true
+                    report_cite("non-existent entry %a in %a",tag,dataset)
+                end
             end
         end
     end
@@ -658,6 +687,13 @@ function commands.btxflush(name,tag,field)
             local manipulator, field = checked(field)
             local value = fields[field]
             if type(value) == "string" then
+                local suffixes = dataset.suffixes[tag]
+                if suffixes then
+                    local suffix = suffixes[field]
+                    if suffix then
+                        value = value .. converters.characters(suffix)
+                    end
+                end
                 context(manipulator and manipulated(manipulator,value) or value)
                 return
             end
@@ -665,6 +701,13 @@ function commands.btxflush(name,tag,field)
             if details then
                 local value = details[field]
                 if type(value) == "string" then
+                    local suffixes = dataset.suffixes[tag]
+                    if suffixes then
+                        local suffix = suffixes[field]
+                        if suffix then
+                            value = value .. converters.characters(suffix)
+                        end
+                    end
                     context(manipulator and manipulated(manipulator,value) or value)
                     return
                 end
@@ -686,6 +729,13 @@ function commands.btxdetail(name,tag,field)
             local manipulator, field = checked(field)
             local value = details[field]
             if type(value) == "string" then
+                local suffixes = dataset.suffixes[tag]
+                if suffixes then
+                    local suffix = suffixes[field]
+                    if suffix then
+                        value = value .. converters.characters(suffix)
+                    end
+                end
                 context(manipulator and manipulated(manipulator,value) or value)
             else
                 report("unknown detail %a of tag %a in dataset %a",field,tag,name)
@@ -706,6 +756,13 @@ function commands.btxfield(name,tag,field)
             local manipulator, field = checked(field)
             local value = fields[field]
             if type(value) == "string" then
+                local suffixes = dataset.suffixes[tag]
+                if suffixes then
+                    local suffix = suffixes[field]
+                    if suffix then
+                        value = value .. converters.characters(suffix)
+                    end
+                end
                 context(manipulator and manipulated(manipulator,value) or value)
             else
                 report("unknown field %a of tag %a in dataset %a",field,tag,name)
@@ -1076,34 +1133,47 @@ end
 
 -- todo: nicer refs
 
-local f_reference   = formatters["r:%s:%s:%s"] -- dataset, instance (block), tag
-local f_destination = formatters["d:%s:%s:%s"] -- dataset, instance (block), tag
-local f_listentry   = formatters["d:%s:%s:%s"] -- dataset, instance (block), tag
-local f_internal    = formatters["internal(%s)"] -- dataset, instance (block), tag
+local f_citereference = formatters["btx:%s"] -- dataset, instance (block), tag
+local f_listreference = formatters["btx:%s:%s:%s"] -- dataset, instance (block), tag
 
 local done = { }
 
-function commands.btxreference(dataset,block,tag,data)
-    local ref = f_reference(dataset,block,tag) -- we just need a unique key
-    if not done[ref] then
+function commands.btxcitereference(internal)
+    local ref = f_citereference(internal) -- we just need a unique key
+    local don = done[ref]
+    if don == nil then
         if trace_references then
-            report_reference("link: %s",ref)
+            report_reference("cite: %s",ref)
         end
         done[ref] = true
-        ctx_btxsetreference(dataset,tag,ref,data)
+        ctx_btxsetcitereference(ref,internal)
+    elseif don then
+        report_reference("duplicate cite: %s, skipped",ref)
+        done[ref] = false
+ -- else
+        -- no more messages
     end
 end
 
+-- we just need a unique key, so we could also use btx:<number> but this
+-- way we have a bit of a check for duplicates
+
 local done = { }
 
-function commands.btxdestination(dataset,block,tag,data)
-    local ref = f_destination(dataset,block,tag) -- we just need a unique key
-    if not done[ref] then
+function commands.btxlistreference(dataset,block,tag,data)
+    local ref = f_listreference(dataset,block,tag)
+    local don = done[ref]
+    if don == nil then
         if trace_references then
-            report_reference("link: %s",ref)
+            report_reference("list: %s",ref)
         end
         done[ref] = true
-        ctx_btxsetreference(dataset,tag,ref,data)
+        ctx_btxsetlistreference(dataset,tag,ref,data)
+    elseif don then
+        report_reference("duplicate link: %s, skipped",ref)
+        done[ref] = false
+ -- else
+        -- no more messages
     end
 end
 
@@ -1227,6 +1297,43 @@ local function compresslist(source)
     local first, last, firstr, lastr
     local target, noftarget, tags = { }, 0, { }
     sort(source,keysorter)
+    -- suffixes
+    local oldvalue = nil
+    local suffix   = 0
+    local function setsuffix(entry,suffix,sortfld)
+        entry.suffix  = suffix
+        local dataset = datasets[entry.dataset]
+        if dataset then
+            local suffixes = dataset.suffixes[entry.tag]
+            if suffixes then
+                suffixes[sortfld] = suffix
+            else
+                dataset.suffixes[entry.tag] = { [sortfld] = suffix }
+            end
+        end
+    end
+    for i=1,#source do
+        local entry   = source[i]
+        local sortfld = entry.sortfld
+        if sortfld then
+            local value = entry.sortkey
+            if value == oldvalue then
+                if suffix == 0 then
+                    suffix = 1
+                    local entry = source[i-1]
+                    setsuffix(entry,suffix,sortfld)
+                end
+                suffix = suffix + 1
+                setsuffix(entry,suffix,sortfld)
+            else
+                oldvalue = value
+                suffix   = 0
+            end
+        else
+            break
+        end
+    end
+    --
     local function flushrange()
         noftarget = noftarget + 1
         if last > first + 1 then
@@ -1303,7 +1410,7 @@ local function processcite(dataset,reference,mark,compress,setup,getter,setter,c
             source[i] = data
         end
         if compress and not badkey then
-            local target = (compressor or compresslist)(source)
+            local target  = (compressor or compresslist)(source)
             local function flush(i,state)
                 local entry = target[i]
                 local first = entry.first
@@ -1336,7 +1443,7 @@ local function processcite(dataset,reference,mark,compress,setup,getter,setter,c
                         ctx_btxsetinternal(internal)
                     end
                     if not setter(entry) then
-                        ctx_btxsetfirst(f_missing(entry.tag))
+                        ctx_btxsetfirst(f_missing(tag))
                     end
                 end
                 ctx_btxsetconcat(state)
@@ -1389,6 +1496,7 @@ local setters = setmetatableindex({},function(t,k)
             internal = internal,
             [k]      = value,
             sortkey  = value,
+            sortfld  = k,
         }
     end
     t[k] = v
@@ -1447,6 +1555,7 @@ end
 
 local function setter(dataset,tag,entry,internal)
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         pages    = getdetail(dataset,tag,"pages"),
@@ -1475,6 +1584,7 @@ end
 local function setter(dataset,tag,entry,internal)
     local text = entry.entries.text
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         num      = text,
@@ -1495,10 +1605,12 @@ end
 local function setter(dataset,tag,entry,internal)
     local year = getfield(dataset,tag,"year")
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         year     = year,
         sortkey  = year,
+        sortfld  = "year",
     }
 end
 
@@ -1515,6 +1627,7 @@ end
 local function setter(dataset,tag,entry,internal)
     local index = getfield(dataset,tag,"index")
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         index    = index,
@@ -1538,6 +1651,7 @@ end
 
 local function setter(dataset,tag,entry,internal)
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         category = getfield(dataset,tag,"category"),
@@ -1560,6 +1674,7 @@ end
 
 local function setter(dataset,tag,entry,internal)
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
     }
@@ -1582,6 +1697,7 @@ end
 
 local function setter(dataset,tag,entry,internal)
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         author   = getfield(dataset,tag,"author"),
@@ -1614,12 +1730,10 @@ local function authorcompressor(found,key)
         local entry    = found[i]
         local author   = entry.author
         local aentries = entries[author]
-        -- will be just entry but for tracing ...
-        entry = { internal = entry.internal, author = author, [key] = entry[key], sortkey = entry.sortkey }
         if aentries then
             aentries[#aentries+1] = entry
         else
-            entries[author] =  { entry }
+            entries[author] = { entry }
         end
     end
     for i=1,#found do
@@ -1632,7 +1746,7 @@ local function authorcompressor(found,key)
             -- already done
         else
             result[#result+1] = entry
-            entry.entries  = aentries
+            entry.entries = aentries
             entries[author] = true
         end
     end
@@ -1650,13 +1764,25 @@ local function authorconcat(target,key,setup)
                 ctx_btxsetinternal(internal)
             end
             ctx_btxsetfirst(first[key] or f_missing(first.tag))
-            ctx_btxsetsecond(entry.last[key])
+            local suffix = entry.suffix
+            local value  = entry.last[key]
+            if suffix then
+                ctx_btxsetsecond(value .. converters.characters(suffix))
+            else
+                ctx_btxsetsecond(value)
+            end
         else
             local internal = entry.internal
             if internal then
                 ctx_btxsetinternal(internal)
             end
-            ctx_btxsetfirst(entry[key] or f_missing(entry.tag))
+            local suffix = entry.suffix
+            local value  = entry[key] or f_missing(entry.tag)
+            if suffix then
+                ctx_btxsetfirst(value .. converters.characters(suffix))
+            else
+                ctx_btxsetfirst(value)
+            end
         end
         ctx_btxsetconcat(state)
         ctx_btxcitesetup(setup)
@@ -1668,8 +1794,12 @@ end
 
 local function authorsingle(entry,key,setup)
     ctx_btxstartsubcite(setup)
+    local internal = entry.internal
+    if internal then
+        ctx_btxsetinternal(internal)
+    end
     ctx_btxsetfirst(entry[key] or f_missing(entry.tag))
-    ctx_btxcitesetup(setup)
+    ctx_btxcitesetup(setup) -- ??
     ctx_btxstopsubcite()
 end
 
@@ -1690,6 +1820,7 @@ end
 local function setter(dataset,tag,entry,internal)
     local text = entry.entries.text
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         author   = getfield(dataset,tag,"author"),
@@ -1716,11 +1847,13 @@ end
 local function setter(dataset,tag,entry,internal)
     local year = getfield(dataset,tag,"year")
     return {
+        dataset  = dataset,
         tag      = tag,
         internal = internal,
         author   = getfield(dataset,tag,"author"),
         year     = year,
-        sortkey  = year and lpegmatch(numberonly,year)
+        sortkey  = year and lpegmatch(numberonly,year),
+        sortfld  = "year",
     }
 end
 
@@ -1762,14 +1895,18 @@ function listvariants.default(dataset,block,tag,variant)
     context("?")
 end
 
+-- function listvariants.num(dataset,block,tag,variant,listindex)
+--     local lst = f_listentry(dataset,block,tag)
+--     local ref = f_reference(dataset,block,tag)
+--     if trace_references then
+--         report_reference("list: %s",lst)
+--     end
+--     -- todo
+--     ctx_btxdirectlink(ref,listindex) -- a goto
+-- end
+
 function listvariants.num(dataset,block,tag,variant,listindex)
-    local lst = f_listentry(dataset,block,tag)
-    local ref = f_reference(dataset,block,tag)
-    if trace_references then
-        report_reference("list: %s",lst)
-    end
-    -- todo
-    ctx_btxdirectlink(ref,listindex) -- a goto
+    context(listindex) -- a goto
 end
 
 function listvariants.short(dataset,block,tag,variant,listindex)
diff --git a/tex/context/base/publ-ini.mkiv b/tex/context/base/publ-ini.mkiv
index 68e185f23..898f286f6 100644
--- a/tex/context/base/publ-ini.mkiv
+++ b/tex/context/base/publ-ini.mkiv
@@ -43,6 +43,10 @@
 \registerctxluafile{publ-ini}{1.001}
 \registerctxluafile{publ-oth}{1.001} % this could become an option
 
+\doiffileelse{publ-fnd.lua}
+  {\registerctxluafile{publ-fnd}{1.001}} % new method (for the moment only local)
+  {}
+
 \unprotect
 
 \startcontextdefinitioncode
@@ -89,14 +93,23 @@
 \unexpanded\def\stopbtxlistentry
   {\csname\??constructionstophandler\currentconstructionhandler\endcsname}
 
+% \unexpanded\setvalue{\??constructiontexthandler\v!btxlist}%
+%   {\begingroup
+%    \useconstructionstyleandcolor\c!headstyle\c!headcolor % move to \currentconstructiontext
+%    \the\everyconstruction
+%    \constructionparameter\c!headcommand
+%      {\strut
+%       \constructionparameter\c!text
+%       \btx_list_reference_inject}%
+%    \endgroup}
+
 \unexpanded\setvalue{\??constructiontexthandler\v!btxlist}%
   {\begingroup
    \useconstructionstyleandcolor\c!headstyle\c!headcolor % move to \currentconstructiontext
    \the\everyconstruction
-   \constructionparameter\c!headcommand
-     {\strut
-      \constructionparameter\c!text
-      \btx_reference_inject}%
+   \goto
+     {\strut\constructionparameter\c!text\btx_list_reference_inject}%
+     [\s!btx:\the\numexpr\locationcount+\plusone]% \nextinternalreference
    \endgroup}
 
 \unexpanded\def\strc_constructions_initialize#1% class instance
@@ -323,8 +336,9 @@
 \newdimen\d_publ_number_width
 %newdimen\d_publ_number_distance
 
-\ifdefined\btxblock   \else \newcount\btxblock   \fi \btxblock\plusone
-\ifdefined\btxcounter \else \newcount\btxcounter \fi
+\ifdefined\btxblock       \else \newcount\btxblock       \fi \btxblock\plusone
+\ifdefined\btxlistcounter \else \newcount\btxlistcounter \fi
+\ifdefined\btxcitecounter \else \newcount\btxcitecounter \fi
 
 \newtoks \everysetupbtxlistplacement % name will change
 \newtoks \everysetupbtxciteplacement % name will change
@@ -333,10 +347,10 @@
 %   {\ctxcommand{btxaddtolist("\currentbtxrendering",\currentlistindex,"btxref")}}
 
 \definelist % only used for selecting
-  [btx]
+  [\s!btx]
 
 \setuplist
-  [btx]%
+  [\s!btx]%
   [\c!state=\v!start]%
 
 \appendtoks
@@ -367,7 +381,7 @@
    \directsetup{\s!btx:\currentbtxalternative:\currentbtxcategory}%
    \removeunwantedspaces
    \ifx\currentbtxcombis\empty \else
-     ;\space % todo .. parameter .. what is a good name .. problem: what if ends with .
+     \btxlistvariantparameter\c!separator
      \processcommacommand[\currentbtxcombis]\btx_entry_inject_combi
    \fi
    \endgroup}
@@ -454,14 +468,14 @@
            keyword      = "\btxrenderingparameter\c!keyword",
        }}%
        % next we analyze the width
-       \ifx\btx_reference_inject_indeed\relax
+       \ifx\btx_list_reference_inject_indeed\relax
        \else
          \edef\p_width{\btxrenderingparameter\c!width}%
          \ifx\p_width\v!auto
-           \scratchcounter\btxcounter
+           \scratchcounter\btxlistcounter
            \setbox\scratchbox\vbox{\settrialtypesetting\ctxcommand{btxfetchlistentries("\currentbtxdataset")}}%
            \d_publ_number_width\wd\scratchbox
-           \global\btxcounter\scratchcounter
+           \global\btxlistcounter\scratchcounter
            \letbtxlistparameter\c!width\d_publ_number_width
          \fi
        \fi
@@ -492,7 +506,7 @@
    \endgroup}
 
 \unexpanded\def\publ_place_list_entry
-  {\global\advance\btxcounter\plusone
+  {\global\advance\btxlistcounter\plusone
    \ifconditional\c_publ_place_register
      \publ_place_list_entry_register
    \fi
@@ -511,32 +525,45 @@
    \fi
    \endgroup}
 
-\unexpanded\def\btxchecklistcombi#1% called at the lua end
-  {\begingroup
-   \edef\currentbtxtag{#1}%
-   ; % todo
-   \publ_check_list_entry
-   \endgroup}
+% \unexpanded\def\btxchecklistcombi#1% called at the lua end
+%   {\begingroup
+%    \edef\currentbtxtag{#1}%
+%    ; % todo
+%    \publ_check_list_entry
+%    \endgroup}
 
 \unexpanded\def\publ_check_list_entry
-  {\global\advance\btxcounter\plusone
+  {\global\advance\btxlistcounter\plusone
    % todo, switch to font
    \hbox{\btx_reference_checked}%
    \par}
 
-\unexpanded\def\btx_reference_inject % we can use a faster \reference
+\unexpanded\def\btx_list_reference_inject % we can use a faster \reference
   {\dontleavehmode\begingroup % no box
      \iftrialtypesetting\else
-       \ctxcommand{btxdestination("\currentbtxdataset","\currentbtxblock","\currentbtxtag","\number\btxcounter")}%
+       \ctxcommand{btxlistreference("\currentbtxdataset","\currentbtxblock","\currentbtxtag","\number\btxlistcounter")}%
      \fi
-     \btx_reference_inject_indeed
+     \btx_list_reference_inject_indeed
    \endgroup}
 
 \unexpanded\def\btx_reference_checked
   {\dontleavehmode\hbox\bgroup
-     \btx_reference_inject_indeed
+     \btx_list_reference_inject_indeed
    \egroup}
 
+\unexpanded\def\btx_cite_reference_inject % todo: wrap whole content?
+  {\dontleavehmode\begingroup % no box
+     \iftrialtypesetting\else
+       \ifx\currentbtxinternal\empty\else
+ %        \global\advance\btxcitecounter\plusone
+         \ctxcommand{btxcitereference(\number\currentbtxinternal)}%
+       \fi
+     \fi
+    % \btx_list_reference_inject_indeed
+   \endgroup}
+
+\let\btxcitereference\btx_cite_reference_inject
+
 \setuvalue{\??btxnumbering\v!short  }{\btxlistvariant{short}} % these will be setups
 \setuvalue{\??btxnumbering\v!bib    }{\btxlistvariant{num}}   % these will be setups
 \setuvalue{\??btxnumbering\s!unknown}{\btxlistvariant{num}}   % these will be setups
@@ -549,13 +576,13 @@
      \letlistparameter\c!textcommand\outdented % needed? we can use titlealign
      \letlistparameter\c!symbol     \v!none
      \letlistparameter\c!aligntitle \v!yes
-     \let\btx_reference_inject_indeed\relax
+     \let\btx_list_reference_inject_indeed\relax
    \else
      \ifcsname\??btxnumbering\p_btx_numbering\endcsname \else
        \let\p_btx_numbering\s!unknown
      \fi
      \letlistparameter\c!headnumber\v!always
-     \expandafter\let\expandafter\btx_reference_inject_indeed\csname\??btxnumbering\p_btx_numbering\endcsname
+     \expandafter\let\expandafter\btx_list_reference_inject_indeed\csname\??btxnumbering\p_btx_numbering\endcsname
    \fi
 \to \everysetupbtxlistplacement
 
@@ -676,7 +703,7 @@
 
 \appendtoks
    \doifnot{\btxrenderingparameter\c!continue}\v!yes
-     {\global\btxcounter\zerocount}%
+     {\global\btxlistcounter\zerocount}%
 \to \everysetupbtxlistplacement
 
 %D When a publication is cited, we need to signal that somehow. This is done with the
@@ -851,7 +878,7 @@
        dataset   = "\currentbtxdataset",%
        reference = "#1",%
        markentry = \iftrialtypesetting false\else true\fi,%
-   }}%
+     }}%
    \fi}
 
 \unexpanded\def\btxmissing#1%
@@ -891,9 +918,14 @@
 \unexpanded\def\btxsetinternal{\def\currentbtxinternal}
 \unexpanded\def\btxsetconcat#1{\setconstant\currentbtxconcat#1\relax}
 
-\unexpanded\def\btxsetreference#1#2% #3#4%
+\unexpanded\def\btxsetlistreference#1#2% #3#4%
   {\strc_references_direct_full_user{btxset="#1",btxref="#2"}}
 
+\unexpanded\def\btxsetcitereference#1#2%
+  {\ifx\currentbtxinternal\empty \else
+     \strc_references_direct_full_user{btxint=#2}{#1}\empty
+   \fi}
+
 \unexpanded\def\btxstartsubcite#1%
   {\bgroup
    \btxcitereset
@@ -912,7 +944,7 @@
   {\begingroup
    \edef\currentbtxlistvariant{#1}%
    \btxlistvariantparameter\c!left
-   \ctxcommand{btxlistvariant("\currentbtxdataset","\currentbtxblock","\currentbtxtag","#1","\number\btxcounter")}% some can go
+   \ctxcommand{btxlistvariant("\currentbtxdataset","\currentbtxblock","\currentbtxtag","#1","\number\btxlistcounter")}% some can go
    \btxlistvariantparameter\c!right
    \endgroup}
 
@@ -1077,7 +1109,8 @@
     \c!right={]}]
 
 \setupbtxlistvariant
-  [\c!namesep={, },
+  [\c!separator={; },
+   \c!namesep={, },
    \c!lastnamesep={ and },
    \c!finalnamesep={ and },
    \c!firstnamesep={ },
diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua
index 3f1fd5c82..d5a2b4d56 100644
--- a/tex/context/base/spac-ver.lua
+++ b/tex/context/base/spac-ver.lua
@@ -50,10 +50,12 @@ local trace_collect_vspacing = false  trackers.register("vspacing.collect",  fun
 local trace_vspacing         = false  trackers.register("vspacing.spacing",  function(v) trace_vspacing         = v end)
 local trace_vsnapping        = false  trackers.register("vspacing.snapping", function(v) trace_vsnapping        = v end)
 local trace_vpacking         = false  trackers.register("vspacing.packing",  function(v) trace_vpacking         = v end)
+local trace_specials         = false  trackers.register("vspacing.specials", function(v) trace_specials         = v end)
 
 local report_vspacing     = logs.reporter("vspacing","spacing")
 local report_collapser    = logs.reporter("vspacing","collapsing")
 local report_snapper      = logs.reporter("vspacing","snapping")
+local report_specials     = logs.reporter("vspacing","specials")
 local report_page_builder = logs.reporter("builders","page")
 
 local a_skipcategory      = attributes.private('skipcategory')
@@ -871,32 +873,72 @@ local overlay  = 9
 
 local special_penalty_min = 32250
 local special_penalty_max = 35000
+local special_penalty_xxx =     0
 
-local function specialpenalty(start,penalty)
- -- nodes.showsimplelist(texlists.page_head,1)
-    local current = find_node_tail(tonut(texlists.page_head)) -- no texlists.page_tail yet
+-- this is rather messy and complex: we want to make sure that successive
+-- header don't break but also make sure that we have at least a decent
+-- break when we have succesive ones (often when testing)
+
+local specialmethods = { }
+local specialmethod  = 1
+
+local properties = nodes.properties.data
+
+specialmethods[1] = function(start,penalty)
+    --
+    if penalty < special_penalty_min or penalty > special_penalty_max then
+        return
+    end
+    -- this can move to the caller
+    local pagehead = tonut(texlists.page_head)
+    local pagetail = find_node_tail(pagehead) -- no texlists.page_tail yet-- no texlists.page_tail yet
+    local current  = pagetail
+    --
+    -- nodes.showsimplelist(pagehead,0)
+    --
+    if trace_specials then
+        report_specials("checking penalty %a",penalty)
+    end
     while current do
         local id = getid(current)
-        if id == glue_code then
-            current = getprev(current)
-        elseif id == penalty_code then
-            local p = getfield(current,"penalty")
-            if p == penalty then
-                if trace_vspacing then
-                    report_vspacing("overloading penalty %a",p)
+        if id == penalty_code then
+            local p = properties[current]
+            if p then
+                local p = p.special_penalty
+                if not p then
+                    if trace_specials then
+                        report_specials("  regular penalty, continue")
+                    end
+                elseif p == penalty then
+                    if trace_specials then
+                        report_specials("  context penalty %a, same level, overloading",p)
+                    end
+                    return special_penalty_xxx
+                elseif p > special_penalty_min and p < special_penalty_max then
+                    if penalty < p then
+                        if trace_specials then
+                            report_specials("  context penalty %a, lower level, overloading",p)
+                        end
+                        return special_penalty_xxx
+                    else
+                        if trace_specials then
+                            report_specials("  context penalty %a, higher level, quitting",p)
+                        end
+                        return
+                    end
+                elseif trace_specials then
+                    report_specials("  context %a, higher level, continue",p)
                 end
-                return current
-            elseif p >= 10000 then
-                current = getprev(current)
-            else
-                break
+            elseif trace_specials then
+                report_specials("  regular penalty, continue")
             end
-        else
-            current = getprev(current)
         end
+        current = getprev(current)
     end
 end
 
+-- specialmethods[2] : always put something before and use that as to-be-changed
+
 local function check_experimental_overlay(head,current) -- todo
     local p = nil
     local c = current
@@ -1005,7 +1047,7 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
     end
     local current, oldhead = head, head
     local glue_order, glue_data, force_glue = 0, nil, false
-    local penalty_order, penalty_data, natural_penalty = 0, nil, nil
+    local penalty_order, penalty_data, natural_penalty, special_penalty = 0, nil, nil, nil
     local parskip, ignore_parskip, ignore_following, ignore_whitespace, keep_together = nil, false, false, false, false
     --
     -- todo: keep_together: between headers
@@ -1014,17 +1056,20 @@ local function collapser(head,where,what,trace,snap,a_snapmethod) -- maybe also
         if penalty_data then
             local p = new_penalty(penalty_data)
             if trace then trace_done("flushed due to " .. why,p) end
-if penalty_data >= 10000 then -- or whatever threshold?
-    local prev = getprev(current)
-    if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue
-            -- tricky case: spacing/grid-007.tex: glue penalty glue
-            head = insert_node_before(head,prev,p)
-    else
-            head = insert_node_before(head,current,p)
-    end
-else
-            head = insert_node_before(head,current,p)
-end
+            if penalty_data >= 10000 then -- or whatever threshold?
+                local prev = getprev(current)
+                if getid(prev) == glue_code then -- maybe go back more, or maybe even push back before any glue
+                        -- tricky case: spacing/grid-007.tex: glue penalty glue
+                    head = insert_node_before(head,prev,p)
+                else
+                    head = insert_node_before(head,current,p)
+                end
+            else
+                head = insert_node_before(head,current,p)
+            end
+-- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
+    properties[p] = { special_penalty = special_penalty or penalty_data }
+-- end
         end
         if glue_data then
             local spec = getfield(glue_data,"spec")
@@ -1113,11 +1158,14 @@ end
                 local so = getattr(current,a_skiporder) or 1 -- has  1 default, no unset (yet)
                 local sp = getattr(current,a_skippenalty)    -- has no default, no unset (yet)
                 if sp and sc == penalty then
-                    if where == "page" and sp >= special_penalty_min and sp <= special_penalty_max then
-                        local previousspecial = specialpenalty(current,sp)
-                        if previousspecial then
-                            setfield(previousspecial,"penalty",0)
-                            sp = 0
+                    if where == "page" then
+                        local p = specialmethods[specialmethod](current,sp)
+                        if p then
+                            if trace then
+                                trace_skip("previous special penalty %a is changed to %a using method %a",sp,p,specialmethod)
+                            end
+                            special_penalty = sp
+                            sp = p
                         end
                     end
                     if not penalty_data then
@@ -1137,7 +1185,7 @@ end
                         current = getnext(current)
                     else
                         -- not look back across head
--- todo: prev can be whatsit (latelua)
+                        -- todo: prev can be whatsit (latelua)
                         local previous = getprev(current)
                         if previous and getid(previous) == glue_code and getsubtype(previous) == userskip_code then
                             local ps = getfield(previous,"spec")
@@ -1383,6 +1431,9 @@ end
         local p = new_penalty(penalty_data)
         if trace then trace_done("result",p) end
         head, tail = insert_node_after(head,tail,p)
+-- if penalty_data > special_penalty_min and penalty_data < special_penalty_max then
+    properties[p] = { special_penalty = special_penalty or penalty_data }
+-- end
     end
     if glue_data then
         if not tail then tail = find_node_tail(head) end
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index 7efcee997..e98aea887 100644
Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
index ea3539ae7..6603d4b5a 100644
Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf differ
diff --git a/tex/context/base/strc-ref.mkvi b/tex/context/base/strc-ref.mkvi
index 359bb4a76..91bcb6193 100644
--- a/tex/context/base/strc-ref.mkvi
+++ b/tex/context/base/strc-ref.mkvi
@@ -268,6 +268,7 @@
      \strc_references_start_destination_nodes
      \setnextinternalreference
      \edef\m_strc_references_user{#user}%
+     \edef\m_strc_references_text{#text}%
      \ctxcommand{setreferenceattribute("\s!full", "\referenceprefix","#labels",
          {
               references = {
@@ -278,9 +279,11 @@
               metadata = {
                   kind = "\s!full",
               },
+          \ifx\m_strc_references_text\empty \else
               entries = {
-                  text = \!!bs#text\!!es
+                  text = \!!bs\m_strc_references_text\!!es
               },
+          \fi
           \ifx\m_strc_references_user\empty \else
               userdata = {
                   \m_strc_references_user
diff --git a/tex/context/base/strc-sec.mkiv b/tex/context/base/strc-sec.mkiv
index 122892104..5adfeba1e 100644
--- a/tex/context/base/strc-sec.mkiv
+++ b/tex/context/base/strc-sec.mkiv
@@ -1012,7 +1012,8 @@
 \def\strc_sectioning_after_yes
   {\ifconditional\headisdisplay
      \ifconditional\c_strc_sectioning_auto_break
-       \vspacing[\v!samepage-\currentheadlevel]%
+     % \vspacing[\v!samepage-\currentheadlevel]%
+\vspacing[\v!samepage]%
      \fi
      \strc_sectioning_empty_correction
      \headparameter\c!after
@@ -1068,10 +1069,16 @@
      \strc_sectioning_handle_page_nop
      \edef\p_aligntitle{\headparameter\c!aligntitle}%
      \ifx\p_aligntitle\v!float
+\ifconditional\c_strc_sectioning_auto_break
+  \vspacing[\v!samepage-\currentheadlevel]%
+\fi
          \headparameter\c!before\relax
          \indent
      \else
          \page_otr_command_flush_side_floats
+\ifconditional\c_strc_sectioning_auto_break
+  \vspacing[\v!samepage-\currentheadlevel]%
+\fi
          \headparameter\c!before\relax
      \fi
      \global\c_strc_sectioniong_preceding_level\currentheadlevel
diff --git a/tex/context/base/task-ini.lua b/tex/context/base/task-ini.lua
index 75ce08232..3dc5e71b4 100644
--- a/tex/context/base/task-ini.lua
+++ b/tex/context/base/task-ini.lua
@@ -24,7 +24,6 @@ local disableaction   = tasks.disableaction
 local freezegroup     = tasks.freezegroup
 local freezecallbacks = callbacks.freeze
 
-
 appendaction("processors",   "normalizers", "typesetters.characters.handler")                    -- always on
 appendaction("processors",   "normalizers", "fonts.collections.process")                         -- disabled
 appendaction("processors",   "normalizers", "fonts.checkers.missing")                            -- disabled
diff --git a/tex/context/base/trac-ctx.mkiv b/tex/context/base/trac-ctx.mkiv
index 3baddede2..3eb53713c 100644
--- a/tex/context/base/trac-ctx.mkiv
+++ b/tex/context/base/trac-ctx.mkiv
@@ -1,4 +1,3 @@
-
 %D \module
 %D   [       file=trac-ctx,
 %D        version=2012.07.13,
diff --git a/tex/context/base/trac-vis.mkiv b/tex/context/base/trac-vis.mkiv
index 694d1b09d..7517037ea 100644
--- a/tex/context/base/trac-vis.mkiv
+++ b/tex/context/base/trac-vis.mkiv
@@ -83,9 +83,16 @@
 \to \t_syst_visualizers_optimize
 
 \unexpanded\def\showmakeup
-  {\ctxcommand{setvisual("makeup")}%
-   \let\normalvtop\ruledtopv
-   \let\vtop      \ruledtopv}
+  {\dosingleempty\syst_visualizers_makeup}
+
+\unexpanded\def\syst_visualizers_makeup[#1]%
+  {\iffirstargument
+     \ctxcommand{setvisual("#1")}%
+   \else
+     \ctxcommand{setvisual("makeup")}%
+     \let\normalvtop\ruledtopv
+     \let\vtop      \ruledtopv
+   \fi}
 
 \unexpanded\def\showallmakeup
   {\ctxcommand{setvisual("all")}%
diff --git a/tex/context/base/x-mathml-html.mkiv b/tex/context/base/x-mathml-html.mkiv
new file mode 100644
index 000000000..2ac7d3cba
--- /dev/null
+++ b/tex/context/base/x-mathml-html.mkiv
@@ -0,0 +1,40 @@
+%D \modul
+%D   [       file=x-mathml,
+%D        version=2014.05.18,
+%D          title=\CONTEXT\ XML Modules,
+%D       subtitle=\MATHML\ embedded HTML,
+%D         author=Hans Hagen,
+%D           date=\currentdate,
+%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
+%C
+%C This module is part of the \CONTEXT\ macro||package and is
+%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
+%C details.
+
+% maybe some more
+
+\startmodule [mathml-html]
+
+\startxmlsetups mml:html:b
+    \bold{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:i
+    \italic{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:tt
+    \mono{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html:em
+    \emphasized{\xmlflush{#1}}
+\stopxmlsetups
+
+\startxmlsetups mml:html
+    \xmlsetsetup{#1}{mml:b|mml:i|mml:tt|mml:em}{mml:html:*}
+\stopxmlsetups
+
+\xmlregistersetup{mml:html}
+
+\stopmodule
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 4dfefd656..8f8a4bb1d 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
 -- merged file : luatex-fonts-merged.lua
 -- parent file : luatex-fonts.lua
--- merge date  : 05/18/14 15:57:13
+-- merge date  : 05/20/14 09:53:13
 
 do -- begin closure to overcome local limits and interference
 
-- 
cgit v1.2.3