texlive[75059] Master/texmf-dist: citation-style-language (29apr25)

commits+karl at tug.org commits+karl at tug.org
Tue Apr 29 21:47:32 CEST 2025


Revision: 75059
          https://tug.org/svn/texlive?view=revision&revision=75059
Author:   karl
Date:     2025-04-29 21:47:32 +0200 (Tue, 29 Apr 2025)
Log Message:
-----------
citation-style-language (29apr25)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md
    trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf
    trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex
    trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.1
    trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-parser.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex2csl.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-journal-data.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-choose.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-group.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-layout.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-number.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-sort.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-unicode.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-yaml.lua
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc.lua
    trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-cite.sty
    trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-compatible.sty
    trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty
    trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty
    trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-ro-RO.xml
    trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-th-TH.xml
    trunk/Master/texmf-dist/tex/latex/citation-style-language/styles/modern-humanities-research-association.csl

Added Paths:
-----------
    trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-lua-uni-words.lua

Modified: trunk/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/doc/latex/citation-style-language/CHANGELOG.md	2025-04-29 19:47:32 UTC (rev 75059)
@@ -7,6 +7,24 @@
 
 ## [Unreleased]
 
+## [0.8.0] - 2025-04-29
+
+### Added
+
+- Convert BibTeX entry keys to NFC
+- The entry keys are case-folded for comparing.
+- Add citation option `unsorted`.
+
+### Fixed
+
+- Fix an infinite loop bug of unrecognized `babel` language name ([#65](https://github.com/zepinglee/citeproc-lua/issues/65)).
+- Bib2csl: The hyphens in `number` fields are correctly escaped when converted to CSL-JSON.
+- Bib2csl: Map `shorthand` field to CSL `citation-label` variable.
+- BibTeX parser: Fix hyphen in family name.
+- Fix a bug in EDTF parsing.
+- Bib2csl: Fix a sentence case conversion bug that words after colons are not capitalized.
+- Fix an error when `hyperref` is loaded before CSL ([#91](https://github.com/zepinglee/citeproc-lua/issues/91)).
+
 ## [0.7.0] - 2025-02-23
 
 ### Added
@@ -80,6 +98,7 @@
 
 - Add support for multiple bibliographies (`refsection` environment).
 - Add global `ref-section` option.
+- Add support for `biber`'s `%`-style inline comment in `.bib` files.
 
 ### Fixed
 
@@ -98,13 +117,13 @@
 - Add `\fullcite` command ([#64](https://github.com/zepinglee/citeproc-lua/issues/64)).
 - Add support for annotated bibliography ([#64](https://github.com/zepinglee/citeproc-lua/issues/64)).
 
-## Changed
+### Changed
 
 - Check if the `\cite` command is in a footnote.
 
 ## [0.4.9] - 2024-04-21
 
-## Added
+### Added
 
 - Add normal paragraph style for list of references ([#60](https://github.com/zepinglee/citeproc-lua/discussions/60)).
 
@@ -281,7 +300,8 @@
 
 - Initial CTAN release.
 
-[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.7.0...HEAD
+[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.8.0...HEAD
+[0.8.0]: https://github.com/zepinglee/citeproc-lua/compare/v0.7.0...v0.8.0
 [0.7.0]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.8...v0.7.0
 [0.6.8]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.7...v0.6.8
 [0.6.7]: https://github.com/zepinglee/citeproc-lua/compare/v0.6.6...v0.6.7

Modified: trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/doc/latex/citation-style-language/citation-style-language-doc.tex	2025-04-29 19:47:32 UTC (rev 75059)
@@ -51,7 +51,7 @@
   }%
 }
 
-\date{2025-02-23 v0.7.0}
+\date{2025-04-29 v0.8.0}
 
 \maketitle
 
@@ -310,6 +310,16 @@
   \cite[prefix = {See }, page = 42]{ITEM-1}
 \end{LaTeXdemo}
 
+\DescribeOption{unsorted}
+Some styles specify the order of items in the citations (e.g., alphabetical
+or chronological) and the \opt{unsorted} option can be used to disable this
+behavior. This is useful in cases like citing secondary sources. The following
+exapmle produces “(Rabitt, 1982, as cited in Lyon 2014)”.
+
+\begin{LaTeXdemo}
+  \cites{rabitt1982}[prefix={, as cited in}, unsorted = true]{lyon2014}
+\end{LaTeXdemo}
+
 \begin{table}
   \centering
   \caption{The locators supported in CSL v1.0.2.}

Modified: trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.1
===================================================================
--- trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.1	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.1	2025-04-29 19:47:32 UTC (rev 75059)
@@ -1,4 +1,4 @@
-.TH citeproc-lua 1 "0.7.0"
+.TH citeproc-lua 1 "0.8.0"
 .SH NAME
 citeproc-lua \- make CSL citations and bibliography for LaTeX
 .SH SYNOPSIS

Modified: trunk/Master/texmf-dist/doc/man/man1/citeproc-lua.man1.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-data.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -478,6 +478,7 @@
     },
     legmaterial = {
       csl = "legislation",
+      notes = "APA §11.6: Legislative materials include federal testimony, hearings, bills, resolutions, reports, and related documents.",
       source = "apa.dbx",
     },
     letter = {
@@ -1696,8 +1697,8 @@
       type = "entrykey",
     },
     entrysubtype = {
-      csl = nil,
-      notes = "Not supported.",
+      csl = "genre",
+      notes = "May be used to determine the entry type (article-magazine).",
       source = "biblatex",
       type = "literal",
     },
@@ -3191,8 +3192,8 @@
       type = "option",
     },
     relatedstring = {
-      csl = nil,
-      notes = "Not supported.",
+      csl = "genre",
+      notes = "The style apa.csl assumes that `genre` is entered as “Review of the book” or similar.",
       source = "biblatex",
       type = "literal",
     },
@@ -3433,8 +3434,8 @@
       type = "literal",
     },
     shorthand = {
-      csl = nil,
-      notes = "Not supported.",
+      csl = "citation-label",
+      notes = "A special designation to be used by the citation style instead of the usual label.",
       source = "biblatex",
       type = "literal",
     },
@@ -3792,7 +3793,7 @@
       type = "literal",
     },
     titleaddon = {
-      csl = nil,
+      csl = "genre",
       source = "biblatex",
       type = "literal",
     },
@@ -4482,6 +4483,7 @@
   },
   primary_fields = {
     annotation = "note",
+    archive = "archive",
     ["archive-place"] = "archive-place",
     author = "author",
     bookauthor = "container-author",

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-parser.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-parser.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex-parser.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -38,12 +38,11 @@
 local Cc = lpeg.Cc
 local Cf = lpeg.Cf
 local Cg = lpeg.Cg
-local Cmt = lpeg.Cmt
-local Cp = lpeg.Cp
 local Ct = lpeg.Ct
 local V = lpeg.V
 
 
+--- at diagnostic disable codestyle-check
 -- Learned from <http://boston.conman.org/2020/06/05.1>.
 local function case_insensitive_pattern(str)
   local char = R("AZ", "az") / function (c) return P(c:lower()) + P(c:upper()) end
@@ -60,8 +59,10 @@
 
 -- Based on the grammar described at <https://github.com/aclements/biblib>.
 local function get_bibtex_grammar()
-  local comment = (1 - P"@")^0
-  local space = S(" \t\r\n")^0
+  local inline_comment = P"%" * (1-P"\n")^0 * P"\n"
+  local comment = (inline_comment + 1 - P"@")^0
+  -- local ws = S(" \t\r\n")^0
+  local ws = (S" \t\r\n" + inline_comment)^0
   local comment_cmd = case_insensitive_pattern("comment")
   local balanced = P{ "{" * V(1)^0 * "}" + (1 - S"{}") }
   local ident = (- R"09") * (R"\x20\x7F" - S" \t\"#%'(),={}")^1
@@ -75,31 +76,31 @@
                     name = string.lower(name),
                   }
                 end
-  local value = Ct(piece * (space * P"#" * space * piece)^0)
+  local value = Ct(piece * (ws * P"#" * ws * piece)^0)
 
-  local string_body = Cg(ident / string.lower, "name") * space * P"=" * space * Cg(value, "contents")
-  local string_cmd = Ct(Cg(Cc"string", "category") * case_insensitive_pattern("string") * space * (
-    P"{" * space * string_body * space * P"}"
-    + P"(" * space * string_body * space * P")"
+  local string_body = Cg(ident / string.lower, "name") * ws * P"=" * ws * Cg(value, "contents")
+  local string_cmd = Ct(Cg(Cc"string", "category") * case_insensitive_pattern("string") * ws * (
+    P"{" * ws * string_body * ws * P"}"
+    + P"(" * ws * string_body * ws * P")"
   ))
 
   local preamble_body = Cg(value, "contents")
-  local preamble_cmd = Ct(Cg(Cc"preamble", "category") * case_insensitive_pattern("preamble") * space * (
-    P"{" * space * preamble_body * space * P"}"
-    + P"(" * space * preamble_body * space * P")"
+  local preamble_cmd = Ct(Cg(Cc"preamble", "category") * case_insensitive_pattern("preamble") * ws * (
+    P"{" * ws * preamble_body * ws * P"}"
+    + P"(" * ws * preamble_body * ws * P")"
   ))
 
   local key = (1 - S", \t}\r\n")^0
   local key_paren = (1 - S", \t\r\n")^0
-  local field_value_pair = (ident / string.lower) * space * P"=" * space * value  -- * record_success_position()
+  local field_value_pair = (ident / string.lower) * ws * P"=" * ws * value  -- * record_success_position()
 
-  local entry_body = Cf(Ct"" * (P"," * space * Cg(field_value_pair))^0 * (P",")^-1, rawset)
-  local entry = Ct(Cg(Cc"entry", "category") * Cg(ident / string.lower, "type") * space * (
-    P"{" * space * Cg(key, "key") * space * Cg(entry_body, "fields")^-1 * space * (P"}")
-    + P"(" * space * Cg(key_paren, "key") * space * Cg(entry_body, "fields")^-1 * space * (P")")
+  local entry_body = Cf(Ct"" * (P"," * ws * Cg(field_value_pair))^0 * (P",")^-1, rawset)
+  local entry = Ct(Cg(Cc"entry", "category") * Cg(ident / string.lower, "type") * ws * (
+    P"{" * ws * Cg(key, "key") * ws * Cg(entry_body, "fields")^-1 * ws * (P"}")
+    + P"(" * ws * Cg(key_paren, "key") * ws * Cg(entry_body, "fields")^-1 * ws * (P")")
   ))
 
-  local command_or_entry = P"@" * space * (comment_cmd + preamble_cmd + string_cmd + entry)
+  local command_or_entry = P"@" * ws * (comment_cmd + preamble_cmd + string_cmd + entry)
 
   -- The P(-1) causes nil parsing result in case of error.
   local bibtex_grammar = Ct(comment * (command_or_entry * comment)^0) * P(-1)
@@ -106,6 +107,7 @@
 
   return bibtex_grammar
 end
+--- at diagnostic enable codestyle-check
 
 
 local function concat_strings(pieces, strings)
@@ -114,7 +116,7 @@
     if type(piece) == "string" then
       value = value .. piece
     elseif type(piece) == "table" and piece.category == "string" then
-      local piece_str = strings[piece.name]
+      local piece_str = strings[bibtex_parser.normalize_key(piece.name)]
       if piece_str then
         value = value .. piece_str
       else
@@ -162,9 +164,8 @@
   return obj
 end
 
---- at alias EntryKey string
 
---- at alias BibtexEntry { key: EntryKey, type: string, fields: table<string, string> }
+--- at alias BibtexEntry { key: string, type: string, fields: table<string, string> }
 --- at class BibtexData
 --- at field entries BibtexEntry[]
 --- at field entries_by_id table<string, BibtexEntry>
@@ -173,7 +174,6 @@
 
 --- at alias Exception table
 
----comment
 --- at param bib_str string
 --- at param strings table<string, string>?
 --- at return BibtexData?
@@ -216,8 +216,9 @@
 
     elseif object.category == "string" then
       local string_value = concat_strings(object.contents, strings)
-      strings[object.name] = string_value
-      res.strings[object.name] = string_value
+      local normalized_name = bibtex_parser.normalize_key(object.name)
+      strings[normalized_name] = string_value
+      res.strings[normalized_name] = string_value
 
     elseif object.category == "preamble" then
       local value = concat_strings(object.contents, strings)
@@ -227,8 +228,8 @@
         res.preamble = value
       end
 
-    -- elseif object.category == "comment" then
-    -- Is this really needed?
+      -- elseif object.category == "comment" then
+      -- Is this really needed?
 
     elseif object.category == "exception" then
       -- TODO
@@ -271,7 +272,7 @@
   return object
 end
 
-
+--- at diagnostic disable codestyle-check
 -- continuation byte
 local utf8_cont = lpeg.R("\128\191")
 
@@ -292,6 +293,7 @@
                + P(1) / function (c) return P(c) end
   return Cf(char^1, function (a, b) return a * b end):match(str)
 end
+--- at diagnostic enable codestyle-check
 
 
 --- at alias NameDict table<string, string>
@@ -301,9 +303,11 @@
 --- at param str NameStr name field value
 --- at return NameStr[]
 function bibtex_parser.split_names(str)
+  --- at diagnostic disable codestyle-check
   local delimiter_and = ignore_case("and") * (space + -1)
   local name = (balanced - space * delimiter_and)^1
   local names = Ct(((white_space * delimiter_and) + C(name))^0)
+  --- at diagnostic enable codestyle-check
   return names:match(str)
 end
 
@@ -314,17 +318,19 @@
   str = util.strip(str)
   if string.match(str, ",$") then
     util.warning(string.format("Name '%s' has has a comma at the end.", str))
-    str = string.gsub(str, ",$", '')
+    str = string.gsub(str, ",$", "")
   end
 
+  --- at diagnostic disable codestyle-check
   local comma = P","
   local comma_part = (balanced - comma)^0
   local comma_parts = Ct(C(comma_part) * (comma * white_space * C(comma_part))^0)
+  --- at diagnostic enable codestyle-check
   local parts = comma_parts:match(str)
 
   local is_biblatex_extended_format = false
   if #parts > 0 and (string.match(parts[1], "^[a-zA-Z]+%-?i?%s*=")
-      or string.match(parts[1], '^"[a-zA-Z]+%-?i?%s*=.*"$')) then
+        or string.match(parts[1], '^"[a-zA-Z]+%-?i?%s*=.*"$')) then
     is_biblatex_extended_format = true
   end
 
@@ -430,8 +436,10 @@
       key = "jr-i"
     end
 
+    --- at diagnostic disable codestyle-check
     local braced_pattern = P"{" * C(balanced^0) * P"}" * P(-1)
     local stripped = braced_pattern:match(value)
+    --- at diagnostic enable codestyle-check
     if stripped then
       value = stripped
     end
@@ -442,13 +450,15 @@
 end
 
 function bibtex_parser._split_first_von_last_parts(str)
-  local word_sep = P"-" + P"~" + P(util.unicode['no-break space'])
+  --- at diagnostic disable codestyle-check
+  local word_sep = P" " + P"~" + P(util.unicode['no-break space'])
   local word_tokens = Ct(C(utf8_balanced - space_char - word_sep)^0)
   local words_and_seps = Ct(word_tokens * (C(space + word_sep) * word_tokens)^0)
   local pieces = words_and_seps:match(str)
+  --- at diagnostic enable codestyle-check
 
   local words = {}
-  local seps = { "" }
+  local seps = {""}
 
   for _, piece in ipairs(pieces) do
     if type(piece) == "table" then
@@ -475,9 +485,6 @@
     end
   end
 
-  -- util.debug(von_start)
-  -- util.debug(von_stop)
-
   local name = {}
 
   if von_stop > 0 then
@@ -496,9 +503,11 @@
 end
 
 function bibtex_parser._split_von_last_parts(str)
+  --- at diagnostic disable codestyle-check
   local word_sep = P"-" + P"~" + P(util.unicode['no-break space'])
   local word_tokens = Ct(C(utf8_balanced - space_char - word_sep)^0)
   local words_and_seps = Ct(word_tokens * (C(space + word_sep) * word_tokens)^0)
+  --- at diagnostic enable codestyle-check
   local pieces = words_and_seps:match(str)
 
   local words = {}
@@ -538,17 +547,24 @@
 --- at param parts string[]
 --- at return NameDict
 function bibtex_parser._split_last_jr_first_parts(parts)
-    local name = bibtex_parser._split_von_last_parts(parts[1])
-    if parts[2] ~= "" then
-      name.jr = parts[2]
-    end
-    if parts[3] ~= "" then
-      name.first = parts[3]
-    end
-    return name
+  local name = bibtex_parser._split_von_last_parts(parts[1])
+  if parts[2] ~= "" then
+    name.jr = parts[2]
+  end
+  if parts[3] ~= "" then
+    name.first = parts[3]
+  end
+  return name
 end
 
 
+function bibtex_parser.normalize_key(entry_key)
+  local normalized_key = unicode.NFC(entry_key)
+  normalized_key = unicode.casefold(normalized_key)
+  return normalized_key
+end
+
+
 --- Note that BibTeX find crossref in a case-insensitive manner (see
 --- `article-crossref` in `xampl.bib`) which is unlike biber/biblatex.
 --- This function is case-sensitive.
@@ -558,7 +574,7 @@
   if not entry_dict then
     entry_dict = {}
     for _, entry in ipairs(entry_list) do
-      entry_dict[entry.key] = entry
+      entry_dict[bibtex_parser.normalize_key(entry.key)] = entry
     end
   end
   for _, entry in ipairs(entry_list) do
@@ -566,7 +582,7 @@
     --- at type string?
     local ref_key = entry.fields.crossref
     while ref_key do
-      local crossref_entry = entry_dict[ref_key]
+      local crossref_entry = entry_dict[bibtex_parser.normalize_key(ref_key)]
       if crossref_entry then
         ref_entry = crossref_entry
         for field, value in pairs(ref_entry.fields) do
@@ -576,7 +592,8 @@
         end
         ref_key = ref_entry.fields.crossref
       else
-        util.warning(string.format("Didn't find a database entry for crossref '%s' in entry '%s'.", ref_key, ref_entry.key))
+        util.warning(string.format("Didn't find a database entry for crossref '%s' in entry '%s'.", ref_key,
+          ref_entry.key))
         ref_entry.fields.crossref = nil
         ref_key = nil
       end
@@ -588,7 +605,6 @@
 bibtex_parser._default_parser = BibtexParser:new()
 
 
----comment
 --- at param bib_str string input string
 --- at param strings table<string, string>? strings
 --- at return BibtexData?, Exception[]

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex2csl.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex2csl.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-bibtex2csl.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -13,7 +13,7 @@
 local latex_parser
 local unicode
 local util
-local using_luatex, kpse = pcall(require, "kpse")
+local using_luatex, _ = pcall(require, "kpse")
 if using_luatex then
   uni_utf8 = require("unicode").utf8
   bibtex_parser = require("citeproc-bibtex-parser")
@@ -33,9 +33,6 @@
 --- at alias CslData CslItem[]
 
 
-local parser = bibtex_parser.BibtexParser:new()
-
-
 ---Parse BibTeX content and convert to CSL-JSON
 --- at param str string
 --- at param keep_unknown_commands boolean? Keep unknown latex markups in <code>.
@@ -43,7 +40,8 @@
 --- at param sentence_case_title boolean? Convert `title` and `booktitle` to sentence case.
 --- at param check_sentence_case boolean? Check titles that are already sentence cased and do not conver them.
 --- at return CslData?, Exception[]
-function bibtex2csl.parse_bibtex_to_csl(str, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case)
+function bibtex2csl.parse_bibtex_to_csl(str, keep_unknown_commands, case_protection, sentence_case_title,
+    check_sentence_case)
   local strings = {}
   for name, macro in pairs(bibtex_data.macros) do
     strings[name] = macro.value
@@ -51,8 +49,10 @@
   local bib_data, exceptions = bibtex_parser.parse(str, strings)
   local csl_json_items = nil
   if bib_data then
+    -- TODO: Ideally we should load all .bib files and then resolve crossrefs and related
     bibtex_parser.resolve_crossrefs(bib_data.entries, bibtex_data.entries_by_id)
-    csl_json_items = bibtex2csl.convert_to_csl_data(bib_data, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case)
+    csl_json_items = bibtex2csl.convert_to_csl_data(bib_data, keep_unknown_commands, case_protection,
+      sentence_case_title, check_sentence_case)
   end
   return csl_json_items, exceptions
 end
@@ -64,19 +64,25 @@
 --- at param sentence_case_title boolean?
 --- at param check_sentence_case boolean?
 --- at return CslData
-function bibtex2csl.convert_to_csl_data(bib, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case)
+function bibtex2csl.convert_to_csl_data(bib, keep_unknown_commands, case_protection, sentence_case_title,
+    check_sentence_case)
   local csl_data = {}
 
   -- BibTeX looks for crossref in a case-insensitive manner.
-  local entries_by_id = {}
+  local bib_entry_dict = {}
+  local csl_item_dict = {}
+
   for _, entry in ipairs(bib.entries) do
-    entries_by_id[unicode.casefold(entry.key)] = entry
-  end
+    bib_entry_dict[unicode.casefold(entry.key)] = entry
+    local item = bibtex2csl.convert_to_csl_item(entry, keep_unknown_commands, case_protection, sentence_case_title,
+      check_sentence_case)
 
-  for _, entry in ipairs(bib.entries) do
-    local item = bibtex2csl.convert_to_csl_item(entry, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case)
     table.insert(csl_data, item)
+    csl_item_dict[item.id] = item
   end
+
+  bibtex2csl.resolve_related(csl_item_dict, bib_entry_dict)
+
   return csl_data
 end
 
@@ -87,7 +93,8 @@
 --- at param sentence_case_title boolean?
 --- at param check_sentence_case boolean?
 --- at return CslItem
-function bibtex2csl.convert_to_csl_item(entry, keep_unknown_commands, case_protection, sentence_case_title, check_sentence_case, disable_journal_abbreviation)
+function bibtex2csl.convert_to_csl_item(entry, keep_unknown_commands, case_protection, sentence_case_title,
+    check_sentence_case, disable_journal_abbreviation)
   --- at type CslItem
   local item = {
     id = entry.key,
@@ -159,6 +166,28 @@
   -- Merge title, maintitle, subtitle, titleaddon
   bibtex2csl.process_titles(entry)
 
+  -- biblatex-apa
+  if entry.fields.howpublished then
+    local howpublished = string.lower(entry.fields.howpublished)
+    if howpublished == "advance online publication" then
+      -- TODO: get_locale_term("advance online publication" )
+      item.status = "Advance online publication"
+    elseif howpublished == "manunpub" then
+      -- biblatex-apa
+      item.genre = "Unpublished manuscript"
+    elseif howpublished == "maninprep" then
+      -- biblatex-apa
+      item.genre = "Manuscript in preparation"
+    elseif howpublished == "mansub" then
+      -- biblatex-apa
+      item.genre = "Manuscript submitted for publication"
+    end
+  end
+  if entry.fields.pubstate
+      and string.lower(entry.fields.pubstate) == "inpress" then
+    -- TODO: get_locale_term("advance online publication" )
+    item.status = "in press"
+  end
 end
 
 
@@ -165,18 +194,22 @@
 --- at param entry BibtexEntry
 function bibtex2csl.process_titles(entry)
   local fields = entry.fields
+  -- title and subtitle
   if fields.subtitle then
-    if not fields.shorttitle then
-      fields.shorttitle = fields.title
-    end
     if fields.title then
       fields.title = util.join_title(fields.title, fields.subtitle)
+      if not fields.shorttitle then
+        fields.shorttitle = fields.title
+      end
     else
       fields.title = fields.subtitle
     end
+    fields.subtitle = nil
   end
+
+  -- booktitle and booksubtitle
   if fields.booksubtitle then
-    if not fields.shorttitle then
+    if not fields["container-title-short"] then
       fields["container-title-short"] = fields.booktitle
     end
     if fields.booktitle then
@@ -184,7 +217,43 @@
     else
       fields.booktitle = fields.booksubtitle
     end
+    fields.booksubtitle = nil
   end
+
+  -- mainsubtitle
+  if fields.mainsubtitle then
+    if fields.maintitle then
+      fields.maintitle = util.join_title(fields.maintitle, fields.mainsubtitle)
+    else
+      fields.maintitle = fields.mainsubtitle
+    end
+  end
+
+  -- maintitle
+  if fields.maintitle then
+    if entry.type == "audio" or entry.type == "video" then
+      -- maintitle is the container-title
+      fields["container-title"] = fields.maintitle
+    elseif fields.booktitle then
+      -- maintitle is with booktitle
+      if not fields["volume-title"] then
+        fields["volume-title"] = fields.booktitle
+        fields.booktitle = fields.maintitle
+      end
+    else
+      -- maintitle is with title
+      if fields.title then
+        if not fields["volume-title"] then
+          fields["volume-title"] = fields.title
+          fields.title = fields.maintitle
+        end
+      else
+        -- This is unlikelu to happen.
+        fields.title = fields.maintitle
+      end
+    end
+  end
+
   if fields.journalsubtitle then
     if fields.journaltitle then
       fields.journaltitle = util.join_title(fields.journaltitle, fields.journalsubtitle)
@@ -191,16 +260,15 @@
     elseif fields.journal then
       fields.journal = util.join_title(fields.journal, fields.journal)
     end
+    fields.journalsubtitle = nil
   end
   if fields.issuesubtitle then
-    if not fields.shorttitle then
-      fields["volume-title-short"] = fields.issuetitle
-    end
     if fields.issuetitle then
       fields.issuetitle = util.join_title(fields.issuetitle, fields.issuesubtitle)
     else
       fields.issuetitle = fields.issuesubtitle
     end
+    fields.issuesubtitle = nil
   end
 end
 
@@ -215,7 +283,8 @@
 --- at param check_sentence_case boolean?
 --- at return string? csl_field
 --- at return string | table | number?  csl_value
-function bibtex2csl.convert_field(bib_field, value, keep_unknown_commands, case_protection, sentence_case_title, language, check_sentence_case)
+function bibtex2csl.convert_field(bib_field, value, keep_unknown_commands, case_protection, sentence_case_title,
+    language, check_sentence_case)
   local field_data = bibtex_data.fields[bib_field]
   if not field_data then
     return nil, nil
@@ -234,7 +303,7 @@
   local field_type = field_data.type
   local csl_value
   if field_type == "name" then
-  -- 1. unicode 2. prify (remove LaTeX markups) 3. plain text 4. split name parts
+    -- 1. unicode 2. prify (remove LaTeX markups) 3. plain text 4. split name parts
     value = latex_parser.latex_to_unicode(value)
     local names = bibtex_parser.split_names(value)
     csl_value = {}
@@ -244,17 +313,19 @@
     end
 
   elseif field_type == "date" then
-    csl_value = latex_parser.latex_to_pseudo_html(value, false, false)
-    csl_value = bibtex2csl._parse_edtf_date(csl_value)
+    if string.match(value, "\\") then
+      -- "{\noopsort{1973c}}1981"
+      value = latex_parser.latex_to_pseudo_html(value, false, false)
+    end
+    -- "-3000~" should not be converted to "-3000 "
+    csl_value = util.parse_edtf(value)
 
   elseif bib_field == "title" or bib_field == "shorttitle"
       or bib_field == "booktitle" or bib_field == "container-title-short" then
-    -- util.debug(value)
     -- 1. unicode 2. sentence case 3. html tag
     if sentence_case_title and (not language or util.startswith(language, "en")) then
-      -- util.debug(value)
-      csl_value = latex_parser.latex_to_sentence_case_pseudo_html(value, keep_unknown_commands, case_protection, check_sentence_case)
-      -- util.debug(csl_value)
+      csl_value = latex_parser.latex_to_sentence_case_pseudo_html(value, keep_unknown_commands, case_protection,
+        check_sentence_case)
     else
       csl_value = latex_parser.latex_to_pseudo_html(value, keep_unknown_commands, case_protection)
     end
@@ -281,10 +352,9 @@
 
 function bibtex2csl.convert_to_csl_name(bibtex_name)
   if bibtex_name.last and not (bibtex_name.first or bibtex_name.von or bibtex_name.jr)
-    and string.match(bibtex_name.last, "^%b{}$") then
-    -- util.debug(bibtex_name)
+      and string.match(bibtex_name.last, "^%b{}$") then
     return {
-      literal = string.sub(bibtex_name.last, 2, -2)
+      literal = string.sub(bibtex_name.last, 2, -2),
     }
   end
   local csl_name = {
@@ -297,11 +367,126 @@
 end
 
 
+local arxiv_url_prefix = "https://arxiv.org/abs/"
+local pubmed_url_prefix = "https://www.ncbi.nlm.nih.gov/pubmed/"
+local pubmed_central_url_prefix = "https://www.ncbi.nlm.nih.gov/pmc/articles/"
+
+
 --- at param item CslItem
 --- at param entry BibtexEntry
 function bibtex2csl.post_process_special_fields(item, entry, disable_journal_abbreviation)
   local bib_type = entry.type
   local bib_fields = entry.fields
+
+  -- biblatex-apa
+  if item.type == "article-journal" and entry.fields.entrysubtype == "nonacademic" then
+    item.type = "article-magazine"
+    item.genre = nil
+
+  elseif item.type == "motion_picture" then
+    if entry.fields.entrysubtype == "tvseries" then
+      item.type = "broadcast"
+      if item.genre == "tvseries" then
+        item.genre = "TV series"
+      end
+    elseif entry.fields.entrysubtype == "tvepisode" then
+      item.type = "broadcast"
+      if item.genre == "tvepisode" then
+        item.genre = "TV series episode"
+      end
+    end
+
+  elseif item.type == "song" then
+    if entry.fields.entrysubtype == "podcast" then
+      item.type = "broadcast"
+      if item.genre == "podcast" then
+        item.genre = "Audio podcast"
+      end
+
+    elseif entry.fields.entrysubtype == "podcastepisode" then
+      item.type = "broadcast"
+      if item.genre == "podcastepisode" then
+        item.genre = "Audio podcast episode"
+      end
+
+    elseif entry.fields.entrysubtype == "interview" then
+      item.type = "interview"
+
+    elseif entry.fields.entrysubtype == "speech" then
+      item.type = "speech"
+
+    end
+
+  elseif item.type == "graphic" then
+    item["archive-place"] = item["publisher-place"]
+
+    if entry.fields.entrysubtype == "map" then
+      item.type = "map"
+    end
+
+  elseif item.type == "webpage" then
+
+    -- eprinttype is mapped to
+    -- - `archive` for Google books;
+    -- - `container-title` for twitter post;
+    -- - `publisher` for arXiv preprint;
+
+    local eprint_type_map = {
+      facebook = "post",
+      instagram = "post",
+      reddit = "post",
+      twitter = "post",
+      arxiv = "preprint",
+      psyarxiv = "preprint",
+      pubmed = "preprint",
+      ["pubmed central"] = "preprint",
+    }
+
+    -- Biblatex's `online` type can be mapped to `post`, `preprint`
+    -- local eprinttype = entry.fields.eprinttype or entry.fields.archiveprefix
+    if item.archive then
+      local eprint_type = eprint_type_map[string.lower(item.archive)]
+      if eprint_type then
+        item.type = eprint_type
+      elseif item.number then
+        item.type = "preprint"
+      elseif entry.fields.eprint then
+        item.type = "preprint"
+        item.numerb = entry.fields.eprint
+      elseif item.DOI then
+        item.type = "preprint"
+      elseif item.URL then
+        if util.startswith(item.URL, arxiv_url_prefix) then
+          item.type = "preprint"
+        elseif util.startswith(item.URL, pubmed_url_prefix) then
+          item.type = "preprint"
+        elseif util.startswith(item.URL, pubmed_central_url_prefix) then
+          item.type = "preprint"
+        end
+      end
+      if item.type == "preprint" then
+        if not item.publisher then
+          item.publisher = item.archive
+          item.archive = nil
+        end
+        if not item.number then
+          item.number = entry.fields.eprint
+        end
+      else
+        if not item["container-title"] then
+          item["container-title"] = item.archive
+          item.archive = nil
+        end
+      end
+    end
+
+  end
+
+  -- event-date
+  if item["event-date"] and not item.issued then
+    item.issued = util.deep_copy(item["event-date"])
+  end
+
   -- event-title: for compatibility with CSL v1.0.1 and earlier versions
   if item["event-title"] then
     item.event = item["event-title"]
@@ -315,6 +500,15 @@
     end
   end
 
+  if not item.genre then
+    if bib_type == "phdthesis" then
+      -- from APA
+      item.genre = "Doctoral dissertation"
+    elseif bib_type == "mastersthesis" then
+      item.genre = "Master’s thesis"
+    end
+  end
+
   -- month
   -- local month = bib_fields.month
   local month_text = bib_fields.month
@@ -350,7 +544,7 @@
     elseif item["collection-title"] and not item["collection-number"] then
       item["collection-number"] = bib_fields.number
     elseif not item.number then
-      item.number = bib_fields.number
+      item.number = string.gsub(bib_fields.number, "([^-])%-([^-])", "%1\\-%2")
     end
   end
 
@@ -359,7 +553,7 @@
     if item.publisher or bib_type == "inproceedings" or bib_type == "proceedings" then
       if not item.organizer then
         item.organizer = {
-          literal = bib_fields.organization
+          literal = bib_fields.organization,
         }
       end
     elseif not item.publisher then
@@ -379,6 +573,46 @@
     item.PMID = bib_fields.eprint
   end
 
+  if item.URL then
+    if item.type == "preprint" and util.startswith(item.URL, arxiv_url_prefix) then
+      if not item.publisher then
+        item.publisher = "arXiv"
+        item.archive = nil
+      end
+      if not item.number then
+        item.number = util.remove_prefix(item.URL, pubmed_url_prefix)
+      end
+    end
+
+    if util.startswith(item.URL, pubmed_url_prefix) then
+      if not item.publisher then
+        item.publisher = "PubMed"
+        item.archive = nil
+      end
+      if not item.PMID then
+        item.PMID = util.remove_prefix(item.URL, pubmed_url_prefix)
+      end
+    end
+
+    if util.startswith(item.URL, pubmed_central_url_prefix) then
+      if not item.publisher then
+        item.publisher = "PubMed Central"
+        item.archive = nil
+      end
+      if not item.PMCID then
+        item.PMCID = util.remove_prefix(item.URL, pubmed_central_url_prefix)
+      end
+    end
+  end
+
+  -- `APA Education [@APAEducation], (2018, June 29). College students are forming menta/-health c/ubs-and they're making a difference @washingtonpost [Thumbnail with link attached]`
+  -- The `[Thumbnail with link attached]` should not be italicized.
+  -- if bib_fields.titleaddon and item.genre and item.genre ~= bib_fields.titleaddon then
+  --   if item.title then
+  --     item.title = string.format("%s [%s]", item.title, bib_fields.titleaddon)
+  --   end
+  -- end
+
 end
 
 
@@ -406,43 +640,59 @@
 end
 
 
-function bibtex2csl._parse_edtf_date(str)
-  local date_range = util.split(str, "/")
-  if str == "" then
-    return nil
-  end
-  if #date_range == 1 then
-    date_range = util.split(str, util.unicode["en dash"])
-  end
+local original_field_dict = {
+  author = "original-author",
+  issued = "original-date",
+  publisher = "original-publisher",
+  ["publisher-place"] = "original-publisher-place",
+  title = "original-title",
+}
 
-  local literal = { literal = str }
+local reviewed_field_dict = {
+  author = "reviewed-author",
+  genre = "reviewed-genre",
+  title = "reviewed-title",
+}
 
-  if #date_range > 2 then
-    return literal
-  end
 
-  local date = {}
-  date["date-parts"] = {}
-  for _, date_part in ipairs(date_range) do
-    local date_ = bibtex2csl._parse_single_date(date_part)
-    if not date_ then
-      return literal
-    end
-    table.insert(date["date-parts"], date_)
-  end
-  return date
-end
+--- at param csl_item_dict table<string, CslItem>
+--- at param bib_entry_dict table<string, BibtexEntry>
+function bibtex2csl.resolve_related(csl_item_dict, bib_entry_dict)
+  for _, entry in pairs(bib_entry_dict) do
+    local related_key = entry.fields.related
+    local related_type = entry.fields.relatedtype
+    if related_key then
+      local related_bib_entry = bib_entry_dict[unicode.casefold(related_key)]
+      if related_bib_entry then
+        local csl_item = csl_item_dict[entry.key]
+        local related_csl_item = csl_item_dict[related_bib_entry.key]
+        if related_type == "reprintof" or related_type == "reprintfrom" then
+          for original_field, new_field in pairs(original_field_dict) do
+            if not csl_item[new_field] and related_csl_item[original_field] then
+              csl_item[new_field] = util.deep_copy(related_csl_item[original_field])
+            end
+          end
 
+        elseif related_type == "translationof" or related_type == "translationfrom" then
+          for original_field, new_field in pairs(original_field_dict) do
+            if not csl_item[new_field] and related_csl_item[original_field] then
+              csl_item[new_field] = util.deep_copy(related_csl_item[original_field])
+            end
+          end
 
-function bibtex2csl._parse_single_date(str)
-  local date = {}
-  for _, date_part in ipairs(util.split(str, "%-")) do
-    if not string.match(date_part, "^%d+$") then
-      return nil
+        elseif related_type == "reviewof" then
+          for reviewed_field, new_field in pairs(reviewed_field_dict) do
+            if not csl_item[new_field] and related_csl_item[reviewed_field] then
+              csl_item[new_field] = util.deep_copy(related_csl_item[reviewed_field])
+            end
+          end
+
+        end
+      else
+        util.warning(string.format('Related entry "%s" of "%s" not found.', related_key, entry.key))
+      end
     end
-    table.insert(date, tonumber(date_part))
   end
-  return date
 end
 
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-cli.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -1,4 +1,3 @@
-
 --
 -- Copyright (c) 2021-2025 Zeping Lee
 -- Released under the MIT license.
@@ -8,40 +7,39 @@
 local cli = {}
 
 
-local lpeg = require("lpeg")
-
 require("lualibs")
 local citeproc_manager = require("citeproc-manager")
 local citeproc = require("citeproc")
-local bibtex2csl  -- = require("citeproc-bibtex-parser")  -- load on demand
+local bibtex2csl  -- = require("citeproc-bibtex2csl")  -- load on demand
 local util = require("citeproc-util")
-local latex_parser = require("citeproc-latex-parser")
 
 
 -- http://lua-users.org/wiki/AlternativeGetOpt
-local function getopt( arg, options )
+local function getopt(arg, options)
   local tab = {}
   for k, v in ipairs(arg) do
-    if string.sub( v, 1, 2) == "--" then
-      local x = string.find( v, "=", 1, true )
-      if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 )
-      else      tab[ string.sub( v, 3 ) ] = true
+    if string.sub(v, 1, 2) == "--" then
+      local x = string.find(v, "=", 1, true)
+      if x then
+        tab[string.sub(v, 3, x - 1)] = string.sub(v, x + 1)
+      else
+        tab[string.sub(v, 3)] = true
       end
-    elseif string.sub( v, 1, 1 ) == "-" then
+    elseif string.sub(v, 1, 1) == "-" then
       local y = 2
       local l = string.len(v)
       local jopt
-      while ( y <= l ) do
-        jopt = string.sub( v, y, y )
-        if string.find( options, jopt, 1, true ) then
+      while (y <= l) do
+        jopt = string.sub(v, y, y)
+        if string.find(options, jopt, 1, true) then
           if y < l then
-            tab[ jopt ] = string.sub( v, y+1 )
+            tab[jopt] = string.sub(v, y + 1)
             y = l
           else
-            tab[ jopt ] = arg[ k + 1 ]
+            tab[jopt] = arg[k + 1]
           end
         else
-          tab[ jopt ] = true
+          tab[jopt] = true
         end
         y = y + 1
       end

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-context.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -10,7 +10,7 @@
 local LocalizedQuotes
 local util
 
-local using_luatex, kpse = pcall(require, "kpse")
+local using_luatex, _ = pcall(require, "kpse")
 if using_luatex then
   unicode = require("citeproc-unicode")
   LocalizedQuotes = require("citeproc-output").LocalizedQuotes
@@ -64,7 +64,7 @@
 function Context:new()
   local o = {
     lang = "en-US",
-    in_bibliography = false
+    in_bibliography = false,
   }
   setmetatable(o, self)
   self.__index = self
@@ -75,8 +75,8 @@
   local variable_type = util.variable_types[name]
   if variable_type == "number" then
     return self:get_number(name)
-  -- elseif variable_type == "date" then
-  --   return self:get_date(name)
+    -- elseif variable_type == "date" then
+    --   return self:get_date(name)
   elseif variable_type == "name" then
     return self:get_name(name)
   else
@@ -260,9 +260,9 @@
       end
       break
     end
-  -- name_ParsedDroppingParticleWithApostrophe.txt
-  -- given: "François Hédelin d'" =>
-  -- given: "François Hédelin", dropping-particle: "d'"
+    -- name_ParsedDroppingParticleWithApostrophe.txt
+    -- given: "François Hédelin d'" =>
+    -- given: "François Hédelin", dropping-particle: "d'"
     if string.match(part, "^%l+'?$") or string.match(part, "^%l+’$") then
       table.insert(dp_parts, 1, part)
     end
@@ -291,7 +291,6 @@
 --     name["non-dropping-particle"] = last_word
 --     name.given = table.concat(util.slice(words, 1, -2), " ")
 --   end
---   util.debug(name)
 -- end
 
 function Context:get_localized_date(form)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-element.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -82,7 +82,7 @@
 function Element:set_attribute(node, attribute)
   local value = node:get_attribute(attribute)
   if value then
-    local key = string.gsub(attribute, "%-" , "_")
+    local key = string.gsub(attribute, "%-", "_")
     self[key] = value
   end
 end
@@ -90,10 +90,10 @@
 function Element:set_bool_attribute(node, attribute)
   local value = node:get_attribute(attribute)
   if value == "true" then
-    local key = string.gsub(attribute, "%-" , "_")
+    local key = string.gsub(attribute, "%-", "_")
     self[key] = true
   elseif value == "false" then
-    local key = string.gsub(attribute, "%-" , "_")
+    local key = string.gsub(attribute, "%-", "_")
     self[key] = false
   end
 end
@@ -101,7 +101,7 @@
 function Element:set_number_attribute(node, attribute)
   local value = node:get_attribute(attribute)
   if value then
-    local key = string.gsub(attribute, "%-" , "_")
+    local key = string.gsub(attribute, "%-", "_")
     self[key] = tonumber(value)
   end
 end
@@ -193,10 +193,7 @@
   local group_var = GroupVar.UnresolvedPlain
 
   for _, child_element in ipairs(self.children) do
-    -- util.debug(child_element.element_name)
     local child_ir = child_element:build_ir(engine, state, context)
-    -- util.debug(child_ir)
-    -- util.debug(child_ir.group_var)
 
     if child_ir then
       -- cs:group and its child elements are suppressed if
@@ -241,8 +238,6 @@
   ir.sort_key = ir_sort_key
   ir.group_var = group_var
 
-  -- util.debug(ir)
-
   return ir
 end
 
@@ -409,7 +404,6 @@
   --   {"1", "",  " & "}
   --   {"5", "8", ", "}
   -- }
-  -- util.debug(number_part_list)
 
   for _, number_parts in ipairs(number_part_list) do
     if form == "roman" then
@@ -457,18 +451,18 @@
     and_text = context.locale:get_simple_term("and") or "and"
     and_symbol = context.locale:get_simple_term("and", "symbol") or "&"
   end
-  -- util.debug(and_symbol)
 
+  --- at diagnostic disable codestyle-check
   local space = l.S(" \t\r\n")
   local delimiter_patt = space^0 * l.P(",") * space^0 * l.P(and_text) * space^1
-    + space^0 * l.P(",") * space^0 * l.P(and_symbol) * space^0
-    + space^0 * l.P(",") * space^0 * l.P("&") * space^0
-    + space^1 * l.P(and_text) * space^1
-    + space^0 * l.P(and_symbol) * space^0
-    + space^0 * l.P("&") * space^0
-    + space^0 * l.P(",") * space^0
-    + space^0 * l.P("-") * space^0
-    + space^0 * l.P(util.unicode["en dash"]) * space^0
+      + space^0 * l.P(",") * space^0 * l.P(and_symbol) * space^0
+      + space^0 * l.P(",") * space^0 * l.P("&") * space^0
+      + space^1 * l.P(and_text) * space^1
+      + space^0 * l.P(and_symbol) * space^0
+      + space^0 * l.P("&") * space^0
+      + space^0 * l.P(",") * space^0
+      + space^0 * l.P("-") * space^0
+      + space^0 * l.P(util.unicode["en dash"]) * space^0
   local delimiter = l.C(delimiter_patt^1) / function (delimiter)
     return {
       type = "delimiter",
@@ -482,8 +476,8 @@
     }
   end
   local grammer = l.Ct((token_patt * (delimiter * token_patt)^0)^-1)
+  --- at diagnostic enable codestyle-check
   local tokens = grammer:match(number)
-  -- util.debug(tokens)
 
   if not tokens then
     return {}
@@ -509,7 +503,7 @@
         token.type = "number"
       else
         stop_index = i
-        if i > 1 and tokens[i-1].type == "delimiter" then
+        if i > 1 and tokens[i - 1].type == "delimiter" then
           stop_index = i - 1
         end
         break
@@ -519,7 +513,7 @@
       if string.match(token.value, "^%s*-%s*$")
           or string.match(token.value, "^%s*–%s*$") then
         token.delimiter_type = "range"
-        if i > 2 and tokens[i-2].delimiter_type == "range" then
+        if i > 2 and tokens[i - 2].delimiter_type == "range" then
           stop_index = i
           break
         end
@@ -551,7 +545,7 @@
   local number_parts = {}
   for i, token in ipairs(tokens) do
     if token.type == "number" then
-      if i == 1 or tokens[i-1].delimiter_type == "and" then
+      if i == 1 or tokens[i - 1].delimiter_type == "and" then
         table.insert(number_parts, {token.value, "", ""})
       else
         number_parts[#number_parts][2] = token.value
@@ -568,7 +562,6 @@
       end
     end
   end
-  -- util.debug(number_parts)
   return number_parts
 end
 
@@ -605,9 +598,9 @@
       end
       -- if string.match(start, "^%a*%d+%a*$") and string.match(stop, "^%a*%d+%a*$") then
       --   if s
-        table.insert(number_part_list, {start, stop, delim})
+      table.insert(number_part_list, {start, stop, delim})
       -- else
-        -- table.insert(number_part_list, {start .. "-" .. stop, "", delim})
+      -- table.insert(number_part_list, {start .. "-" .. stop, "", delim})
       -- end
     else
       table.insert(number_part_list, {start, stop, delim})
@@ -664,7 +657,7 @@
     return stop
   end
 
-  local start_prefix, start_num  = string.match(start, "^(.-)(%d+)$")
+  local start_prefix, start_num = string.match(start, "^(.-)(%d+)$")
   local stop_prefix, stop_num = string.match(stop, "^(.-)(%d+)$")
   if start_prefix ~= stop_prefix then
     -- Not valid range: "n11564-1568" -> "n11564-1568"
@@ -692,7 +685,6 @@
   number_parts[2] = stop
 end
 
----comment
 --- at param start string
 --- at param stop string
 --- at return string
@@ -736,15 +728,11 @@
   return string.sub(start, 1, #start - #stop) .. stop
 end
 
----comment
 --- at param start string
 --- at param stop string
 --- at param threshold integer? Number of minimal digits
 --- at return string
 function Element:_format_range_minimal(start, stop, threshold)
-  -- util.debug(start)
-  -- util.debug(stop)
-  -- util.debug(threshold)
   threshold = threshold or 1
   if #start < #stop then
     return stop
@@ -757,7 +745,6 @@
     end
   end
   local res = string.sub(stop, -threshold)
-  -- util.debug(res)
   return res
 end
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-engine.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -15,7 +15,7 @@
 local output
 local util
 
-local using_luatex, kpse = pcall(require, "kpse")
+local using_luatex, _ = pcall(require, "kpse")
 if using_luatex then
   dom = require("luaxml-domobject")
   context = require("citeproc-context")
@@ -41,8 +41,6 @@
 local Locale = node_locale.Locale
 local Context = context.Context
 local IrState = context.IrState
-local InlineElement = output.InlineElement
--- local OutputFormat = output.OutputFormat
 local LatexWriter = output.LatexWriter
 local HtmlWriter = output.HtmlWriter
 local SortStringFormat = output.SortStringFormat
@@ -78,6 +76,7 @@
 --- at field mode string?
 --- at field prefix string?
 --- at field suffix string?
+--- at field unsorted boolean?
 
 
 --- at class NameVariable
@@ -138,7 +137,6 @@
 --- at field retrieveLocale fun(LanguageCode): string?
 --- at field retrieveItem fun(ItemId): ItemData?
 
----comment
 --- at param sys table
 --- at param style string
 --- at param lang string?
@@ -208,7 +206,7 @@
     locale_tags_info_dict = {},
   }
 
-  setmetatable(o, { __index = CiteProc })
+  setmetatable(o, {__index = CiteProc})
   return o
 end
 
@@ -224,7 +222,6 @@
 
 --- at param ids CiteId[]
 function CiteProc:updateItems(ids)
-  -- util.debug(string.format('updateItems(%s)', table.concat(ids, ", ")))
   self.registry.reflist = {}
   self.registry.registry = {}
   self.person_names = {}
@@ -316,7 +313,6 @@
 --- at param citations_post PostCitation[]
 --- at return [table, [integer, string, CitationId][]]
 function CiteProc:processCitationCluster(citation, citations_pre, citations_post)
-  -- util.debug(string.format('processCitationCluster(%s)', citation.citationID))
   self:_check_valid_citation_element()
   citation = self:_normalize_citation_input(citation)
   self:_check_input(citation, citations_pre, citations_post)
@@ -323,7 +319,7 @@
 
   local citation_list, item_ids = self:_build_reconstituted_citation_list(citation, citations_pre, citations_post)
   self:updateItems(item_ids)
-  if #citation.sorted_items > 1 and self.style.citation.sort then
+  if #citation.sorted_items > 1 and self.style.citation.sort and not citation.properties.unsorted then
     citation.sorted_items = self.style.citation:sorted_citation_items(citation.citationItems, self)
   end
 
@@ -347,7 +343,6 @@
 --- at param citation CitationData
 --- at return string
 function CiteProc:process_citation(citation)
-  -- util.debug(string.format('process_citation(%s)', citation.citationID))
   self:_check_valid_citation_element()
   citation = self:_normalize_citation_input(citation)
 
@@ -359,10 +354,10 @@
 
   local citation_list, item_ids = self:_build_reconstituted_citation_list(citation, citations_pre, {})
   -- self:updateItems(item_ids)
-  for i, cite_item in ipairs(citation.citationItems) do
+  for _, cite_item in ipairs(citation.citationItems) do
     self:get_item(cite_item.id)
   end
-  if #citation.sorted_items > 1 and self.style.citation.sort then
+  if #citation.sorted_items > 1 and self.style.citation.sort and not citation.properties.unsorted then
     citation.sorted_items = self.style.citation:sorted_citation_items(citation.citationItems, self)
   end
 
@@ -435,6 +430,9 @@
     citation_element = self.style.intext
   end
 
+  if #items > 1 and self.style.citation.sort then
+    items = self.style.citation:sorted_citation_items(items, self)
+  end
   local res = citation_element:build_cluster(items, self)
 
   -- local context = {
@@ -448,7 +446,7 @@
     citationItems = items,
     properties = {
       noteIndex = 0,
-    }
+    },
   }
   return res
 end
@@ -507,7 +505,7 @@
     if self.style.info and self.style.info.independent_parent then
       util.error(string.format("This is a dependent style linked to '%s'.", self.style.info.independent_parent))
     else
-      util.error('No <citation> in style.')
+      util.error("No <citation> in style.")
     end
   end
 end
@@ -594,11 +592,13 @@
       local name = string.format("citationsPre[%d]", i)
       table.insert(citation_info_list, {citation_id, note_index, chapter_number, name})
     end
-    table.insert(citation_info_list, {citation.citationID, citation.properties.noteIndex, citation.properties.chapterIndex or 0, "citation"})
+    table.insert(citation_info_list,
+      {citation.citationID, citation.properties.noteIndex, citation.properties.chapterIndex or 0, "citation"})
     for i, post_citation in ipairs(citations_post) do
       local citation_id = post_citation[1]
       local note_index = post_citation[2]
-      local chapter_number = post_citation[3] or self.registry.citations_by_id[citation_id].properties.chapterIndex or 0
+      local chapter_number = post_citation[3] or self.registry.citations_by_id[citation_id].properties.chapterIndex or
+          0
       local name = string.format("citationsPost[%d]", i)
       table.insert(citation_info_list, {citation_id, note_index, chapter_number, name})
     end
@@ -763,7 +763,8 @@
           near_note = cite_item.near_note,
         }
 
-        self:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref, last_ref, num_citations_in_note)
+        self:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref,
+          last_ref, num_citations_in_note)
 
         if self:_check_tainted_position_change(cite_item, position_properties) then
           tainted_citation_ids[citation.citationID] = true
@@ -820,7 +821,8 @@
   return tainted_citation_ids
 end
 
-function CiteProc:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref, last_ref, num_citations_in_note)
+function CiteProc:_set_cite_item_position(cite_item, note_index, previous_cite, previous_citation, citation, first_ref,
+    last_ref, num_citations_in_note)
   -- https://citeproc-js.readthedocs.io/en/latest/csl-json/markup.html#citations
   -- Citations within the main text of the document have a noteIndex of zero.
   if citation.properties.mode == "author-only" or citation.properties.mode == "full-cite" then
@@ -838,7 +840,8 @@
     cite_item.position_level = Position.First
   end
 
-  local preceding_cite_item = self:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index, num_citations_in_note)
+  local preceding_cite_item = self:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index,
+    num_citations_in_note)
 
   if preceding_cite_item then
     cite_item.position_level = self:_get_ibid_position(cite_item, preceding_cite_item)
@@ -854,7 +857,8 @@
 end
 
 -- Find the preceding cite referencing the same item
-function CiteProc:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index, num_citations_in_note)
+function CiteProc:_find_preceding_ibid_item(cite_item, previous_cite, previous_citation, note_index,
+    num_citations_in_note)
   if previous_cite then
     -- a. the current cite immediately follows on another cite, within the same
     --    citation, that references the same item
@@ -968,10 +972,6 @@
 function CiteProc:match_bibsection_object(item, bibsection_object)
   local field = bibsection_object.field
   local value = bibsection_object.value
-  -- util.debug(item.id)
-  -- util.debug(field)
-  -- util.debug(value)
-  -- util.debug(item[field])
   local match = false
   if value == "" then
     if not item[field] or item[field] == "" then
@@ -993,7 +993,6 @@
   if bibsection_object.negative then
     match = not match
   end
-  -- util.debug(match)
   return match
 end
 
@@ -1111,7 +1110,7 @@
     el = element_class:from_node(node)
   end
   if el then
-    for i, child in ipairs(node:get_children()) do
+    for _, child in ipairs(node:get_children()) do
       if child:is_element() then
         local child_element = CiteProc.create_element_tree(child)
         if child_element then
@@ -1180,7 +1179,6 @@
     local note_fields = {}
     local note_lines = {}
     for _, line in ipairs(util.split(item.note, "%s*\r?\n%s*")) do
-      -- util.debug(line)
       local field, value = string.match(line, "^([%w-_ ]+):%s*(.*)$")
       if field then
         local variable_type = util.variable_types[field]
@@ -1205,7 +1203,7 @@
     for field, value in pairs(note_fields) do
       item[field] = value
     end
-    item.note = table.concat(note_lines, '\n')
+    item.note = table.concat(note_lines, "\n")
   end
   return item
 end

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-ir-node.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -107,6 +107,15 @@
   level = level or 0
   local ir_info_str = ""
   local ir_info = {}
+  if self.group_var == GroupVar.Plain then
+    table.insert(ir_info, "[Plain]")
+  elseif self.group_var == GroupVar.Important then
+    table.insert(ir_info, "[Important]")
+  elseif self.group_var == GroupVar.Missing then
+    table.insert(ir_info, "[Missing]")
+  elseif self.group_var == GroupVar.UnresolvedPlain then
+    table.insert(ir_info, "[UnresolvedPlain]")
+  end
   if self.delimiter then
     table.insert(ir_info, string.format('delimiter: "%s"', self.delimiter))
   end
@@ -117,7 +126,7 @@
     table.insert(ir_info, "person_name_irs: " .. tostring(#self.person_name_irs))
   end
   if #ir_info > 0 then
-    ir_info_str = string.format("{%s}", table.concat(ir_info, ' '))
+    ir_info_str = string.format("{%s}", table.concat(ir_info, " "))
   end
   local element_info = self._element.element_name
   for _, attr in ipairs({"name", "variable"}) do
@@ -125,7 +134,8 @@
       element_info = element_info .. string.format(' %s="%s"', attr, self._element[attr])
     end
   end
-  local text = string.format("\n%s [%s] %s <%s> %s", string.rep("    ", level), self.group_var, self._type, element_info, ir_info_str)
+  local text = string.format("\n%s [%s] %s <%s> %s", string.rep("    ", level), self.group_var, self._type,
+    element_info, ir_info_str)
   if self.children and #self.children > 0 then
     for _, child_ir in ipairs(self.children) do
       text = text .. child_ir:_debug(level + 1)
@@ -144,7 +154,6 @@
 end
 
 function IrNode:capitalize_first_term()
-  -- util.debug(self)
   if self._type == "Rendered" and self._element then
     local element = self._element
     --- at cast element Text
@@ -264,7 +273,6 @@
 -- end
 
 
-
 irnode.IrNode = IrNode
 irnode.Rendered = Rendered
 irnode.YearSuffix = YearSuffix

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-journal-data.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-journal-data.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-journal-data.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -5410,6 +5410,7 @@
   ["archives médicales de l'ouest: angers/nantes/rennes"] = "Arch Med Ouest",
   ["archives méditerranéennes de médecine"] = "Arch Mediterr Med",
   ["archives neerlandaises des sciences exactes et naturelles"] = "Arch. Neerl. Sci. Exactes Nat.",
+  ["archives neerlandaises des sciences exactes et naturelles. serie ii"] = "Arch. Neerl. Sci. Exactes Nat. Ser. II",
   ["archives neerlandaises sci exactes et naturelles"] = "Arch. Neerl. Sci. Exactes Nat.",
   ["archives néerlandaises de physiologie de l'homme et des animaux"] = "Arch Neerl Physiol Homme Anim",
   ["archives of academic emergency medicine"] = "Arch Acad Emerg Med",
@@ -14128,6 +14129,7 @@
   ["crystallography reviews"] = "Crystallogr. Rev.",
   ["csa journal (denver, colo.)"] = "CSA J (Denver)",
   ["cscw : proceedings of the conference on computer-supported cooperative work. conference on computer-supported cooperative work"] = "CSCW Conf Comput Support Coop Work",
+  ["csd communications"] = "CSDS Commun.",
   ["csee journal of power and energy systems"] = "CSEE J. Power Energy Syst",
   ["csh protocols"] = "CSH Protoc",
   ["csiam transactions on applied mathematics"] = "CSIAM Trans. Appl. Math.",
@@ -19592,6 +19594,7 @@
   ["frontiers in systems neuroscience"] = "Front Syst Neurosci",
   ["frontiers in the history of science"] = "Front. Hist. Sci.",
   ["frontiers in the internet of things"] = "Front. Internet Things",
+  ["frontiers in theoretical physics"] = "Front. Theor. Phys.",
   ["frontiers in thermal engineering"] = "Front. Therm. Eng.",
   ["frontiers in toxicology"] = "Front. Toxicol.",
   ["frontiers in transplantation"] = "Front. Transplant.",
@@ -25139,6 +25142,7 @@
   ["international journal of modern physics"] = "Int. J. Modern Phys. D",
   ["international journal of modern physics a"] = "Int. J. Mod. Phys. A",
   ["international journal of modern physics a. particles and fields. gravitation. cosmology"] = "Internat. J. Modern Phys. A",
+  ["international journal of modern physics a. particles and fields. gravitation. cosmology. astrophysics. accelerator physics"] = "Internat. J. Modern Phys. A",
   ["international journal of modern physics a: particles and fields; gravitation; cosmology; nuclear physics"] = "Int. J. Mod. Phys. A",
   ["international journal of modern physics b"] = "Int. J. Mod. Phys. B",
   ["international journal of modern physics b: condensed matter physics, statistical physics, applied physics"] = "Int. J. Mod. Phys. B",
@@ -25703,6 +25707,7 @@
   ["international journal of travel medicine and global health"] = "Int J Travel Med Glob Health",
   ["international journal of trichology"] = "Int J Trichology",
   ["international journal of tropical agriculture = antarrāshtriya ūshnakatibandhīya kr̥shi-śodha patrikā"] = "Int J Trop Agric",
+  ["international journal of tropical and subtropical horticulture"] = "Int. J. Trop. Subtrop. Hortic.",
   ["international journal of tropical disease & health"] = "Int J Trop Dis Health",
   ["international journal of tropical insect science"] = "Int. J. Trop. Insect Sci.",
   ["international journal of tryptophan research"] = "Int. J. Tryptophan Res.",
@@ -41328,6 +41333,7 @@
   ["orbis economicus"] = "Orbis Econ.",
   ["orbis terrarum. internationale zeitschrift für historische geographie der alten welt"] = "OrbTerr",
   ["orbit (amsterdam, netherlands)"] = "Orbit",
+  ["orbita mathematicae"] = "Orbita Math.",
   ["orchids (west palm beach, fla.)"] = "Orchids",
   ["order (dordrecht, netherlands)"] = "Order (Dordr)",
   ["order. a journal on the theory of ordered sets and its applications"] = "Order",
@@ -51776,6 +51782,7 @@
   ["summer computer simulation conference : (scsc 2014) : 2014 summer simulation multi-conference : monterey, california, usa, 6-10 july 2014. summer computer simulation conference (2014 : monterey, calif.)"] = "Summer Comput Simul Conf (2014)",
   ["summer computer simulation conference : (scsc 2015) : 2015 summer simulation multi-conference (summersim'15) : chicago, illinois, usa, 26-29 july 2015. summer computer simulation conference (2015 : chicago, illinois)"] = "Summer Comput Simul Conf (2015)",
   ["summit on translational bioinformatics"] = "Summit Transl Bioinform",
+  ["sums readings"] = "SUMS Read.",
   ["sun (baltimore, md. : 1837)"] = "Sun",
   ["sunday times (london, england : 1931)"] = "Sunday Times",
   ["suny series in ancient greek philosophy"] = "SUNY Ser. Anc. Greek Philos.",
@@ -52213,6 +52220,7 @@
   ["targeted oncology"] = "Target Oncol",
   ["tartu ülikooli toimetised"] = "Tartu Ül. Toimetised",
   ["tata institute of fundamental research lectures on mathematics and physics"] = "Tata Inst. Fund. Res. Lectures on Math. and Phys.",
+  ["tata institute of fundamental research studies in mathematics"] = "Tata Inst. Fundam. Res. Stud. Math.",
   ["tatra mountains mathematical publications"] = "Tatra Mt. Math. Publ.",
   ["tập chí khoa học kỹ thuật nông lâm nghiệp"] = "Tap Chi Khoa Hoc Ky Thuat Nong Lam Nghiep",
   ["tāksūnūmī va bīyūsīstimātīk"] = "Taksunumi Biyusistimatik",
@@ -53294,6 +53302,7 @@
   ["the european journal of surgery = acta chirurgica"] = "Eur J Surg",
   ["the european journal of surgery. supplement. : = acta chirurgica. supplement"] = "Eur J Surg Suppl",
   ["the european journal of the history of economic thought"] = "Eur J Hist Econ Thought",
+  ["the european journal on artificial intelligence"] = "Eur. J. Artif. Intell.",
   ["the european legacy, toward new paradigms : journal of the international society for the study of european ideas"] = "Eur Leg Towar New Paradig",
   ["the european physical journal"] = "Eur. Phys. J. D",
   ["the european physical journal a"] = "Eur. Phys. J. A",
@@ -64364,6 +64373,7 @@
   ["arch natschutz landschforsch"] = "Archiv für Naturschutz und Landschaftsforschung",
   ["arch neerl physiol homme anim"] = "Archives néerlandaises de physiologie de l'homme et des animaux",
   ["arch neerl sci exactes nat"] = "Archives Neerlandaises des Sciences Exactes et Naturelles",
+  ["arch neerl sci exactes nat ser ii"] = "Archives Neerlandaises des Sciences Exactes et Naturelles. Serie II",
   ["arch neurobiol (madr)"] = "Archivos de neurobiologiá",
   ["arch neurocir"] = "Archivos de neurocirugía",
   ["arch neurol"] = "Archives of Neurology",
@@ -71718,6 +71728,7 @@
   ["crystals (basel)"] = "Crystals",
   ["csa j (denver)"] = "CSA journal (Denver, Colo.)",
   ["cscw conf comput support coop work"] = "CSCW : proceedings of the Conference on Computer-Supported Cooperative Work. Conference on Computer-Supported Cooperative Work",
+  ["csds commun"] = "CSD Communications",
   ["csee j power energy syst"] = "CSEE Journal of Power and Energy Systems",
   ["csh protoc"] = "CSH protocols",
   ["csiam trans appl math"] = "CSIAM Transactions on Applied Mathematics",
@@ -74830,6 +74841,7 @@
   ["eur j appl physiol occup physiol"] = "European journal of applied physiology and occupational physiology",
   ["eur j archaeol"] = "European journal of archaeology",
   ["eur j arrhythm electrophysiol"] = "European journal of arrhythmia & electrophysiology",
+  ["eur j artif intell"] = "The European Journal on Artificial Intelligence",
   ["eur j basic appl histochem"] = "European journal of basic and applied histochemistry",
   ["eur j behav anal"] = "European journal of behavior analysis",
   ["eur j biochem"] = "European Journal of Biochemistry",
@@ -76602,6 +76614,7 @@
   ["front synaptic neurosci"] = "Frontiers in synaptic neuroscience",
   ["front syst biol"] = "Frontiers in Systems Biology",
   ["front syst neurosci"] = "Frontiers in systems neuroscience",
+  ["front theor phys"] = "Frontiers in Theoretical Physics",
   ["front therm eng"] = "Frontiers in Thermal Engineering",
   ["front toxicol"] = "Frontiers in Toxicology",
   ["front transplant"] = "Frontiers in Transplantation",
@@ -82251,6 +82264,7 @@
   ["int j trop agric"] = "International journal of tropical agriculture = Antarrāshtriya ūshnakatibandhīya kr̥shi-śodha patrikā",
   ["int j trop dis health"] = "International journal of tropical disease & health",
   ["int j trop insect sci"] = "International Journal of Tropical Insect Science",
+  ["int j trop subtrop hortic"] = "International Journal of Tropical and Subtropical Horticulture",
   ["int j tryptophan res"] = "International journal of tryptophan research : IJTR",
   ["int j tuberc lung dis"] = "The international journal of tuberculosis and lung disease : the official journal of the International Union against Tuberculosis and Lung Disease",
   ["int j turbo jet engines"] = "International Journal Of Turbo And Jet Engines",
@@ -96740,6 +96754,7 @@
   ["orale implantol"] = "Orale Implantologie",
   ["orange cty dent soc bull"] = "Orange County Dental Society Bulletin",
   ["orbis econ"] = "Orbis Economicus",
+  ["orbita math"] = "Orbita Mathematicae",
   ["order (dordr)"] = "Order (Dordrecht, Netherlands)",
   ["ordre natl chir dent cons natl bull off"] = "Bulletin officiel du Conseil national de l'Ordre",
   ["ore energy resour geol"] = "Ore and Energy Resource Geology",
@@ -105632,6 +105647,7 @@
   ["summer comput simul conf (2014)"] = "Summer Computer Simulation Conference : (SCSC 2014) : 2014 Summer Simulation Multi-Conference : Monterey, California, USA, 6-10 July 2014. Summer Computer Simulation Conference (2014 : Monterey, Calif.)",
   ["summer comput simul conf (2015)"] = "Summer Computer Simulation Conference : (SCSC 2015) : 2015 Summer Simulation Multi-Conference (SummerSim'15) : Chicago, Illinois, USA, 26-29 July 2015. Summer Computer Simulation Conference (2015 : Chicago, Illinois)",
   ["summit transl bioinform"] = "Summit on translational bioinformatics",
+  ["sums read"] = "SUMS Readings",
   ["sunday times"] = "Sunday times (London, England : 1931)",
   ["sunday times magazine"] = "The Sunday times magazine",
   ["suny ser anc greek philos"] = "SUNY Series in Ancient Greek Philosophy",
@@ -106038,6 +106054,7 @@
   ["targeted oncol"] = "Targeted Oncology",
   ["tartu ül toimetised"] = "Tartu Ülikooli Toimetised",
   ["tata inst fund res lectures on math and phys"] = "Tata Institute of Fundamental Research Lectures on Mathematics and Physics",
+  ["tata inst fundam res stud math"] = "Tata Institute of Fundamental Research Studies in Mathematics",
   ["tatra mt math publ"] = "Tatra Mountains Mathematical Publications",
   ["taylor & francis syst control book ser"] = "The Taylor & Francis Systems and Control Book Series",
   ["tạp chi y te cong cong"] = "Tạp chí y tế công cộng",

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-latex-parser.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -6,7 +6,6 @@
 
 local latex_parser = {}
 
-local bibtex_parser
 local latex_data
 local markup
 local util
@@ -27,9 +26,7 @@
 -- Convert LaTeX string to Unicode string
 function latex_parser.latex_to_unicode(str)
   local ast = latex_parser.latex_grammar:match(str)
-  -- util.debug(ast)
   latex_parser.convert_ast_to_unicode(ast)
-  -- util.debug(ast)
   local res = latex_parser.to_string(ast)
   return res
 end
@@ -47,10 +44,8 @@
 -- Convert LaTeX string to string with HTML-like tags
 function latex_parser.latex_to_pseudo_html(str, strict, case_protection)
   local ast = latex_parser.latex_grammar:match(str)
-  -- util.debug(ast)
   latex_parser.convert_ast_to_unicode(ast)
   local inlines = latex_parser.convert_tokens_to_inlines(ast, strict, case_protection)
-  -- util.debug(inlines)
   local pseudo_html_format = markup.PseudoHtml:new()
   local res = pseudo_html_format:write_inlines(inlines, {})
   return res
@@ -61,19 +56,18 @@
 -- case_protection: BibTEX case protection with curly braces
 
 
----comment
 --- at param str string
 --- at param keep_unknown_commands boolean?
 --- at param case_protection boolean?
 --- at param check_sentence_case boolean?
 --- at return string
-function latex_parser.latex_to_sentence_case_pseudo_html(str, keep_unknown_commands, case_protection, check_sentence_case)
+function latex_parser.latex_to_sentence_case_pseudo_html(str, keep_unknown_commands, case_protection,
+    check_sentence_case)
   local ast = latex_parser.latex_grammar:match(str)
   latex_parser.convert_ast_to_unicode(ast)
   local inlines = latex_parser.convert_tokens_to_inlines(ast, keep_unknown_commands, case_protection)
   local pseudo_html_format = markup.PseudoHtml:new()
   pseudo_html_format:convert_sentence_case(inlines, check_sentence_case)
-  -- util.debug(inlines)
   local res = pseudo_html_format:write_inlines(inlines, {})
   return res
 end
@@ -93,14 +87,10 @@
   local R = lpeg.R
   local S = lpeg.S
   local C = lpeg.C
-  local Cc = lpeg.Cc
-  local Cf = lpeg.Cf
-  local Cg = lpeg.Cg
-  local Cmt = lpeg.Cmt
-  local Cp = lpeg.Cp
   local Ct = lpeg.Ct
   local V = lpeg.V
 
+  --- at diagnostic disable: codestyle-check
   local space = S(" \t\r\n")^0
   local specials = P"~" / util.unicode["no-break space"]
                   --  + P"\\$" / "$"
@@ -142,6 +132,7 @@
       }
     end,
   }
+  --- at diagnostic enable: codestyle-check
   return latex_grammar
 end
 
@@ -157,7 +148,7 @@
   ["\\enquote"] = "quote",
   ["\\textsc"] = "sc",
   ["\\sout"] = "strike",  -- from ulem package
-  ["\\st"] = "strike", -- from soul package
+  ["\\st"] = "strike",  -- from soul package
   ["\\textsuperscript"] = "sup",
   ["\\textsubscript"] = "sub",
 }
@@ -175,10 +166,9 @@
 }
 
 
-
 function latex_parser.to_string(ast)
   local res = ""
-  for i, token in ipairs(ast) do
+  for _, token in ipairs(ast) do
     if type(token) == "string" then
       res = res .. token
     elseif type(token) == "table" then
@@ -210,7 +200,6 @@
   end
 end
 
----comment
 --- at param tokens any
 --- at param i any
 --- at param command_info string | table
@@ -304,7 +293,7 @@
     if inline_type then
       local arg_inlines
       if command_info.num_args == 1 then
-        local next_token = tokens[i+1]
+        local next_token = tokens[i + 1]
         if type(next_token) == "table" and next_token.type == "group" then
           arg_inlines = latex_parser.convert_tokens_to_inlines(next_token.contents, strict, false)
           if case_protection then
@@ -316,7 +305,7 @@
         i = i + 1
       else  -- command_info.num_args == 0
         if command_info.inline_type then
-          local arg_tokens = util.slice(tokens, i+1, #tokens)
+          local arg_tokens = util.slice(tokens, i + 1, #tokens)
           i = #tokens
           arg_inlines = latex_parser.convert_tokens_to_inlines(arg_tokens, strict, case_protection)
         end
@@ -484,7 +473,7 @@
   for i = #inlines, 1, -1 do
     if i > 1 then
       local inline = inlines[i]
-      local prev = inlines[i-1]
+      local prev = inlines[i - 1]
       if type(inline) == "table" and inline._type == "Code" and
           type(prev) == "table" and prev._type == "Code" then
         prev.value = prev.value .. inline.value
@@ -497,7 +486,6 @@
 end
 
 
-
 function latex_parser.convert_ast_to_rich_text(tokens)
   local res = {}
 
@@ -600,20 +588,16 @@
 
 function latex_parser.parse_seq(str)
   local P = lpeg.P
-  local R = lpeg.R
   local S = lpeg.S
   local C = lpeg.C
-  local Cc = lpeg.Cc
-  local Cf = lpeg.Cf
-  local Cg = lpeg.Cg
-  local Cmt = lpeg.Cmt
-  local Cp = lpeg.Cp
   local Ct = lpeg.Ct
   local V = lpeg.V
 
+  --- at diagnostic disable: codestyle-check
   local balanced = P{"{" * V(1) ^ 0 * "}" + (P"\\{" + P"\\}" + 1 - S"{}")}
   local item = P"{" * C(balanced^0) * P"}" + C((balanced - P",")^1)
   local seq = Ct((item * P(",")^-1)^0)
+  --- at diagnostic enable: codestyle-check
 
   return seq:match(str)
 end
@@ -628,6 +612,7 @@
   local Ct = lpeg.Ct
   local V = lpeg.V
 
+  --- at diagnostic disable: codestyle-check
   local balanced = P{"{" * V(1)^0 * "}" + (P"\\{" + P"\\}" + 1 - S"{}")}
   local key = (R"09" + R"AZ" + R"az" + S"-_./")^1
   local value = (P"{" * C(balanced^0) * P"}" + C((balanced - S",=")^0)) / function (s)
@@ -642,6 +627,7 @@
   local space = S(" \t\r\n")^0
   local pair = C(key) * space * P"=" * space * value * (P(",") * space)^-1
   local prop = Cf(Ct"" * space * Cg(pair)^0, rawset)
+  --- at diagnostic enable: codestyle-check
 
   -- if not str then
   --   print(debug.traceback())

Added: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-lua-uni-words.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-lua-uni-words.lua	                        (rev 0)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-lua-uni-words.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -0,0 +1,316 @@
+--- at diagnostic disable
+-- lua-uni-words.lua
+-- Copyright 2020--2023 Marcel Krüger
+--
+-- This work may be distributed and/or modified under the
+-- conditions of the LaTeX Project Public License, either version 1.3
+-- of this license or (at your option) any later version.
+-- The latest version of this license is in
+--   http://www.latex-project.org/lppl.txt
+-- and version 1.3 or later is part of all distributions of LaTeX
+-- version 2005/12/01 or later.
+--
+-- This work has the LPPL maintenance status `maintained'.
+--
+-- The Current Maintainer of this work is Marcel Krüger
+
+local extended_pictographic, property do
+  local p = require'lua-uni-parse'
+  local l = lpeg or require'lpeg'
+
+  extended_pictographic = p.parse_file('emoji-data',
+    l.Cg(p.fields(p.codepoint_range, 'Extended_Pictographic' * l.Cc(true))) + p.ignore_line,
+    p.multiset)
+  if not extended_pictographic then
+    error[[Break Property matching failed]]
+  end
+
+  property = p.parse_file('WordBreakProperty',
+    l.Cg(p.fields(p.codepoint_range, l.C(l.R('az', 'AZ', '__')^1))) + p.ignore_line,
+    p.multiset)
+  if not property then
+    error[[Break Property matching failed]]
+  end
+end
+
+local ignorable = { Extend = true, Format = true, ZWJ = true, }
+local controls = { CR = true, LF = true, Newline = true, }
+
+local function context_AHLetter_Mid(cp)
+  local prop = property[cp]
+  if ignorable[prop] then
+    return nil, context_AHLetter_Mid
+  end
+  if prop == 'ALetter' then
+    return false, 'ASTARTED'
+  end
+  if prop == 'Hebrew_Letter' then
+    return false, 'HSTARTED'
+  end
+  return true, 'PRE'
+end
+
+local function context_HLetter_Double(cp)
+  local prop = property[cp]
+  if ignorable[prop] then
+    return nil, context_HLetter_Double
+  end
+  if prop == 'Hebrew_Letter' then
+    return false, 'HSTARTED'
+  end
+  return true, 'PRE'
+end
+
+local function context_Numeric_Mid(cp)
+  local prop = property[cp]
+  if ignorable[prop] then
+    return nil, context_Numeric_Mid
+  end
+  if prop == 'Numeric' then
+    return false, 'NSTARTED'
+  end
+  return true, 'PRE'
+end
+
+local state_map state_map = {
+  START = function(prop)
+    if prop == 'CR' then
+      return 'CR', true
+    end
+    if prop == 'LF' or prop == 'Newline' then
+      return 'START', true
+    end
+    return state_map.PRE(prop), true
+  end,
+  PRE = function(prop)
+    if controls[prop] then
+      return state_map.START(prop)
+    end
+    if ignorable[prop] then
+      return 'PRE', false
+    end
+    if prop == 'WSegSpace' then
+      return 'WHITE', true
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', true
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', true
+    end
+    if prop == 'Numeric' then
+      return 'NSTARTED', true
+    end
+    if prop == 'Katakana' then
+      return 'KSTARTED', true
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', true
+    end
+    if prop == 'Regional_Indicator' then
+      return 'RI', true
+    end
+    return 'PRE', true
+  end,
+  CR = function(prop)
+    if prop == 'LF' then
+      return 'START', false
+    else
+      return state_map.START(prop)
+    end
+  end,
+  WHITE = function(prop)
+    if prop == 'WSegSpace' then
+      return 'WHITE', false
+    else
+      return state_map.PRE(prop)
+    end
+  end,
+  EXTEND = function(prop)
+    if ignorable[prop] then
+      return 'EXTEND', false
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', false
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', false
+    end
+    if prop == 'Katakana' then
+      return 'KSTARTED', false
+    end
+    if prop == 'Numeric' then
+      return 'NSTARTED', false
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', false
+    end
+    return state_map.PRE(prop)
+  end,
+  KSTARTED = function(prop)
+    if ignorable[prop] then
+      return 'KSTARTED', false
+    end
+    if prop == 'Katakana' then
+      return 'Katakana', false
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', false
+    end
+    return state_map.PRE(prop)
+  end,
+  RI = function(prop)
+    if ignorable[prop] then
+      return 'RI', false
+    end
+    if prop == 'Regional_Indicator' then
+      return 'PRE', false
+    end
+    return state_map.PRE(prop)
+  end,
+  ASTARTED = function(prop)
+    if ignorable[prop] then
+      return 'ASTARTED', false
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', false
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', false
+    end
+    if prop == 'Numeric' then
+      return 'NSTARTED', false
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', false
+    end
+    if prop == 'MidLetter' or prop == 'MidNumLet' or prop == 'Single_Quote' then
+      return context_AHLetter_Mid
+    end
+    return state_map.PRE(prop)
+  end,
+  HSTARTED = function(prop)
+    if ignorable[prop] then
+      return 'HSTARTED', false
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', false
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', false
+    end
+    if prop == 'Numeric' then
+      return 'NSTARTED', false
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', false
+    end
+    if prop == 'Single_Quote' then
+      return 'HSINGLE_QUOTE', false
+    end
+    if prop == 'MidLetter' or prop == 'MidNumLet' then
+      return context_AHLetter_Mid
+    end
+    if prop == 'Double_Quote' then
+      return context_HLetter_Double
+    end
+    return state_map.PRE(prop)
+  end,
+  HSINGLE_QUOTE = function(prop)
+    if ignorable[prop] then
+      return 'HSINGLE_QUOTE', false
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', false
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', false
+    end
+    return state_map.PRE(prop)
+  end,
+  NSTARTED = function(prop)
+    if ignorable[prop] then
+      return 'NSTARTED', false
+    end
+    if prop == 'ALetter' then
+      return 'ASTARTED', false
+    end
+    if prop == 'Hebrew_Letter' then
+      return 'HSTARTED', false
+    end
+    if prop == 'Numeric' then
+      return 'NSTARTED', false
+    end
+    if prop == 'ExtendNumLet' then
+      return 'EXTEND', false
+    end
+    if prop == 'MidNum' or prop == 'MidNumLet' or prop == 'Single_Quote' then
+      return context_Numeric_Mid
+    end
+    return state_map.PRE(prop)
+  end,
+}
+
+local from_ZWJ, to_ZWJ = {}, {}
+for k in next, state_map do
+  local zwj_state = 'ZWJ_' .. k
+  from_ZWJ[zwj_state], to_ZWJ[k] = k, zwj_state
+end
+
+
+-- The value of "state" is considered internal and should not be relied upon.
+-- Just pass it to the function as is or pass nil. `nil` should only be passed when the passed codepoint starts a new cluster
+local function read_codepoint(cp, state)
+  local mapped_state = from_ZWJ[state]
+  local new_word
+  local prop = property[cp]
+  state, new_word = state_map[mapped_state or state or 'START'](prop)
+  if mapped_state and extended_pictographic[cp] then
+    new_word = false
+  end
+  if prop == 'ZWJ' then
+    state = to_ZWJ[state]
+  end
+  return new_word, state
+end
+
+-- A Lua iterator for strings -- Only reporting the beginning of every word segment
+local function word_boundaries_start(str)
+  local nextcode, str, i = utf8.codes(str)
+  local state = "START"
+  local saved_i, saved_code
+  return function()
+    local new_word, code
+    repeat
+      i, code = nextcode(str, i)
+      if saved_i then
+        new_word, state = state(code)
+        if new_word ~= nil then
+          i, code, saved_i, saved_code = saved_i, saved_code, nil, nil
+        end
+      else
+        if not i then return end
+        new_word, state = read_codepoint(code, state)
+        if new_word == nil then
+          saved_i, saved_code = i, code
+        end
+      end
+    until new_word
+    return i, code
+  end
+end
+-- A more useful iterator: returns the byterange of the segment in reverse order followed by a string with the word
+local function word_boundaries(str)
+  local iter = word_boundaries_start(str)
+  return function(_, cur)
+    if cur == #str then return end
+    local new = iter()
+    if not new then return #str, cur + 1, str:sub(cur + 1) end
+    return new - 1, cur + 1, str:sub(cur + 1, new - 1)
+  end, nil, iter() - 1
+end
+return {
+  read_codepoint = read_codepoint,
+  word_boundaries_start = word_bounaries_start,
+  word_boundaries = word_boundaries,
+}


Property changes on: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-lua-uni-words.lua
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-manager.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -16,7 +16,6 @@
 local json_decode = utilities.json.tolua
 
 local citeproc = require("citeproc")
-local bibtex_data = require("citeproc-bibtex-data")
 local bibtex_parser = require("citeproc-bibtex-parser")
 local latex_parser = require("citeproc-latex-parser")
 local util = citeproc.util
@@ -123,7 +122,7 @@
         util.warning(string.format("Didn't find a database entry for '%s'", id))
       end
       return res
-    end
+    end,
   }
   return citeproc_sys
 end
@@ -205,7 +204,8 @@
           if item_dict[entry.key] then
             util.warning(string.format("Duplicate entry key '%s' in '%s'.", entry.key, file))
           else
-            self.bibtex_entries[entry.key] = entry
+            local normalized_key = bibtex_parser.normalize_key(entry.key)
+            self.bibtex_entries[normalized_key] = entry
             if entry.fields.crossref then
               table.insert(self.entries_with_crossref, entry)
               self.item_index[entry.key] = #items + 1
@@ -393,7 +393,8 @@
 
   local fixture_output = {}
   local function make_block(section, str)
-    return string.format(">>===== %s =====>>\n%s\n<<===== %s =====<<\n", string.upper(section), str, string.upper(section))
+    return string.format(">>===== %s =====>>\n%s\n<<===== %s =====<<\n", string.upper(section), str,
+      string.upper(section))
   end
   for _, section in ipairs({"mode", "description", "result", "bibliography_result", "citations", "citation-items", "csl", "input", "version"}) do
     if not (section == "bibliography_result" and fixture[section] == "") then
@@ -596,9 +597,7 @@
     return
   end
 
-  -- util.debug(citation_info)
   local citation = self:_make_citation(citation_info)
-  -- util.debug(citation)
 
   table.insert(self.ref_section.citations, citation)
 
@@ -610,7 +609,6 @@
   -- else
   citation_str = engine:process_citation(citation)
   -- end
-  -- util.debug(citation_str)
 
   -- tex.sprint(citation_str)
   -- tex.setcatcode(35, 12)  -- #
@@ -656,7 +654,6 @@
 --- at return CitationData
 function CslCitationManager:_make_citation(citation_info)
   -- `citation_info`: "citationID={ITEM-1 at 2},citationItems={{id={ITEM-1},label={page},locator={6}}},properties={noteIndex={3}}"
-  -- util.debug(citation_info)
   local citation = parse_latex_prop(citation_info)
   -- assert(citation.citationID)
   -- assert(citation.citationItems)
@@ -672,9 +669,7 @@
     end
 
     if citation_item.prefix then
-      -- util.debug(citation_item.prefix)
       citation_item.prefix = latex_parser.latex_to_pseudo_html(citation_item.prefix, true, false)
-      -- util.debug(citation_item.prefix)
     end
     if citation_item.suffix then
       citation_item.suffix = latex_parser.latex_to_pseudo_html(citation_item.suffix, true, false)
@@ -708,7 +703,6 @@
     citation.properties.suffix = latex_parser.latex_to_pseudo_html(citation.properties.suffix, true, false)
   end
 
-  -- util.debug(citation)
   return citation
 end
 
@@ -766,7 +760,6 @@
   if filter_str and filter_str ~= "" then
     options = latex_parser.parse_prop(filter_str)
     filter = self:_parser_filter(filter_str)
-    -- util.debug(filter)
   end
   local result = engine:makeBibliography(filter)
 
@@ -775,7 +768,7 @@
 
   --- at type table<string, any>
   local bib_options = {
-    index = options.index or "1"
+    index = options.index or "1",
   }
 
   local bib_option_map = {
@@ -801,11 +794,13 @@
   local bib_lines = {}
 
   if #params.entry_ids > 0 then
-    local entry_ids_line = string.format("\\csloptions{%d}{entry-ids = {%s}}", self.ref_section.index, table.concat(params.entry_ids, ", "))
+    local entry_ids_line = string.format("\\csloptions{%d}{entry-ids = {%s}}", self.ref_section.index,
+      table.concat(params.entry_ids, ", "))
     table.insert(bib_lines, entry_ids_line)
   end
   if #params.excluded_ids > 0 then
-    local excluded_ids_line = string.format("\\csloptions{%d}{excluded-ids = {%s}}", self.ref_section.index, table.concat(params.excluded_ids, ", "))
+    local excluded_ids_line = string.format("\\csloptions{%d}{excluded-ids = {%s}}", self.ref_section.index,
+      table.concat(params.excluded_ids, ", "))
     table.insert(bib_lines, excluded_ids_line)
   end
 
@@ -880,7 +875,7 @@
   {"\\@input", 1},
 }
 
-local balanced = lpeg.P{ "{" * ((1 - lpeg.S"{}") + lpeg.V(1))^0 * "}" }
+local balanced = lpeg.P {"{" * ((1 - lpeg.S "{}") + lpeg.V(1)) ^ 0 * "}"}
 
 --- at param text string
 --- at param num_args integer?
@@ -887,7 +882,7 @@
 --- at return string[]
 local function get_command_arguments(text, command, num_args)
   num_args = num_args or 1
-  local grammar = lpeg.P(command) * lpeg.Ct((lpeg.S(" \t\r\n")^0 * lpeg.C(balanced))^num_args)
+  local grammar = lpeg.P(command) * lpeg.Ct((lpeg.S(" \t\r\n") ^ 0 * lpeg.C(balanced)) ^ num_args)
   local arguments = grammar:match(text)
   if not arguments then
     return {}
@@ -905,7 +900,7 @@
 --- at return table
 function CslCitationManager:_parser_filter(filter_str)
   local conditions = {}
-  for i, condition in ipairs(latex_parser.parse_seq(filter_str)) do
+  for _, condition in ipairs(latex_parser.parse_seq(filter_str)) do
     local negative
     local field, value = string.match(condition, "(%w+)%s*=%s*{([^}]+)}")
     if field then
@@ -925,12 +920,10 @@
       end
     end
   end
-  -- util.debug(conditions)
   return {select = conditions}
 end
 
 
-
 --- at param aux_content string
 --- at return string
 function CslCitationManager:read_aux_file(aux_content)
@@ -958,8 +951,8 @@
     elseif command == "\\csl at aux@cite" then
       self:register_citation_info(tostring(ref_section_index), content)
       -- TODO: refsection bib resources
-    -- elseif command == "\\csl at aux@bibliography" then
-    --   -- table.insert(ref_section.bibliography_configs, content)
+      -- elseif command == "\\csl at aux@bibliography" then
+      --   -- table.insert(ref_section.bibliography_configs, content)
     end
   end
 
@@ -1010,7 +1003,8 @@
           ref_section.engine:enable_linking()
         end
         local style_class = ref_section.engine:get_style_class()
-        local style_class_setup = string.format("\\csloptions{%d}{class = {%s = %s}}", ref_section_index, ref_section.style_id, style_class)
+        local style_class_setup = string.format("\\csloptions{%d}{class = {%s = %s}}", ref_section_index,
+          ref_section.style_id, style_class)
         table.insert(output_lines, style_class_setup)
       end
       self:register_citation_info(tostring(ref_section_index), content)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-bibliography.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -33,7 +33,6 @@
 local Context = context.Context
 local IrState = context.IrState
 local Element = element.Element
-local IrNode = ir_node.IrNode
 local Rendered = ir_node.Rendered
 local SeqIr = ir_node.SeqIr
 local GroupVar = ir_node.GroupVar
@@ -56,7 +55,7 @@
   hanging_indent = false,
   line_spacing = 1,
   entry_spacing = 1,
-  subsequent_author_substitute_rule = "complete-all"
+  subsequent_author_substitute_rule = "complete-all",
 })
 
 function Bibliography:from_node(node, style)
@@ -106,47 +105,43 @@
   return o
 end
 
----comment
 --- at param id string
 --- at param engine CiteProc
 --- at return string?
 function Bibliography:build_bibliography_str(id, engine)
-    local output_format = engine.output_format
+  local output_format = engine.output_format
 
-    local state = IrState:new()
-    local context = Context:new()
-    context.engine = engine
-    context.style = engine.style
-    context.area = self
-    context.in_bibliography = true
-    context.name_inheritance = self.name_inheritance
-    context.format = output_format
-    context.id = id
-    context.cite = nil
-    context.reference = engine:get_item(id)
+  local state = IrState:new()
+  local context = Context:new()
+  context.engine = engine
+  context.style = engine.style
+  context.area = self
+  context.in_bibliography = true
+  context.name_inheritance = self.name_inheritance
+  context.format = output_format
+  context.id = id
+  context.cite = nil
+  context.reference = engine:get_item(id)
 
-    -- CSL-M: `layout` extension
-    local active_layout, context_lang = util.get_layout_by_language(self, engine, context.reference)
-    context.lang = context_lang
-    context.locale = engine:get_locale(context_lang)
+  -- CSL-M: `layout` extension
+  local active_layout, context_lang = util.get_layout_by_language(self, engine, context.reference)
+  context.lang = context_lang
+  context.locale = engine:get_locale(context_lang)
 
-    local ir = self:build_ir(engine, state, context, active_layout)
-    -- util.debug(ir)
-    ir.reference = context.reference
+  local ir = self:build_ir(engine, state, context, active_layout)
+  ir.reference = context.reference
 
-    -- Add year-suffix
-    self:add_bibliography_year_suffix(ir, engine)
+  -- Add year-suffix
+  self:add_bibliography_year_suffix(ir, engine)
 
-    -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt
-    if not ir then
-      return nil
-    end
+  -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt
+  if not ir then
+    return nil
+  end
 
-    -- util.debug(ir)
-    local flat = ir:flatten(output_format)
-    -- util.debug(flat)
-    local str = output_format:output_bibliography_entry(flat, context)
-    return str
+  local flat = ir:flatten(output_format)
+  local str = output_format:output_bibliography_entry(flat, context)
+  return str
 end
 
 function Bibliography:build_ir(engine, state, context, active_layout)
@@ -154,7 +149,6 @@
     util.error("Missing bibliography layout.")
   end
   local ir = active_layout:build_ir(engine, state, context)
-  -- util.debug(ir)
   if self.second_field_align == "flush" and #ir.children >= 2 then
     ir.children[1].display = "left-margin"
     local right_inline_ir = SeqIr:new(util.slice(ir.children, 2), self)
@@ -238,7 +232,6 @@
       ir.first_name_ir.group_var = GroupVar.Missing
     else
       -- the output of label is not substituted
-      -- util.debug(ir.first_name_ir)
       ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)}
     end
   end
@@ -333,13 +326,10 @@
     return
   end
 
-  local year_suffix_number = ir.reference.year_suffix_number
-
   if not ir.year_suffix_irs then
     ir.year_suffix_irs = ir:collect_year_suffix_irs()
     if #ir.year_suffix_irs == 0 then
       local year_ir = ir:find_first_year_ir()
-      -- util.debug(year_ir)
       if year_ir then
         local year_suffix_ir = YearSuffix:new({}, engine.style.citation)
         table.insert(year_ir.children, year_suffix_ir)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-choose.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-choose.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-choose.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -50,7 +50,7 @@
   local ir = SeqIr:new({}, self)
   --- at case ir SeqIr
   ir.should_inherit_delim = true
-  ir.group_var = GroupVar.UnresolvedPlain
+  ir.group_var = GroupVar.Missing
 
   for _, child in ipairs(self.children) do
     if child:evaluate_conditions(engine, state, context) then
@@ -70,8 +70,6 @@
     end
   end
 
-  -- util.debug(ir)
-
   if not context.disambiguate then
     context.disambiguate = true
 
@@ -104,7 +102,7 @@
   local o = {
     condition = condition,
     value = value,
-    match_type = match_type or "all"
+    match_type = match_type or "all",
   }
   setmetatable(o, self)
   self.__index = self
@@ -117,9 +115,10 @@
 --- at field conditions Condition[]
 local If = Element:derive("if", {
   conditions = nil,
-  match = "all"
+  match = "all",
 })
 
+--- at return If
 function If:new()
   local o = Element.new(self)
   o.conditions = {}
@@ -147,14 +146,25 @@
   return o
 end
 
+--- at param node Node
+--- at param attribute string
 function If:add_conditions(node, attribute)
   local values = node:get_attribute(attribute)
   if not values then
     return
   end
-  for _, value in ipairs(util.split(values)) do
-    local condition = Condition:new(attribute, value, self.match)
+  if attribute == "type" then
+    local type_dict = {}
+    for _, value in ipairs(util.split(values)) do
+      type_dict[value] = true
+    end
+    local condition = Condition:new(attribute, type_dict, self.match)
     table.insert(self.conditions, condition)
+  else
+    for _, value in ipairs(util.split(values)) do
+      local condition = Condition:new(attribute, value, self.match)
+      table.insert(self.conditions, condition)
+    end
   end
 end
 
@@ -223,7 +233,6 @@
 
 function If:evaluate_conditions(engine, state, context)
   local res = false
-  -- util.debug(self.conditions)
   for _, condition in ipairs(self.conditions) do
     if self:evaluate_condition(condition, state, context) then
       if self.match == "any" then
@@ -245,8 +254,6 @@
 end
 
 function If:evaluate_condition(condition, state, context)
-  -- util.debug(context.cite)
-  -- util.debug(condition)
   if condition.condition == "disambiguate" then
     return context.disambiguate or (context.in_bibliography and context.reference.disambiguate)
 
@@ -255,7 +262,8 @@
     local variable_type = util.variable_types[variable] or "standard"
 
     if variable_type ~= "standard" and variable_type ~= "number" then
-      util.warning(string.format("Expecting number variable for condition 'is-numeric', got %s '%s'", variable_type, variable))
+      util.warning(string.format("Expecting number variable for condition 'is-numeric', got %s '%s'", variable_type,
+        variable))
       return false
     end
 
@@ -274,7 +282,8 @@
     local variable_type = util.variable_types[variable] or "standard"
 
     if variable_type ~= "date" then
-      util.warning(string.format("Expecting date variable for condition 'is-uncertain-date', got '%s'", variable_type, variable))
+      util.warning(string.format("Expecting date variable for condition 'is-uncertain-date', got '%s'", variable_type,
+        variable))
       return false
     end
     local value = context:get_variable(variable)
@@ -299,7 +308,7 @@
 
   elseif condition.condition == "type" then
     local item_type = context:get_variable("type")
-    return item_type == condition.value
+    return condition.value[item_type] ~= nil
 
   elseif condition.condition == "variable" then
     local var = condition.value
@@ -325,7 +334,6 @@
 end
 
 function If:check_position(position, context)
-  -- util.debug(context.cite)
   if context.in_bibliography then
     return false
   end

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-citation.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -50,9 +50,6 @@
 local Position = util.Position
 
 
--- local DEBUG_DISAMBIGUATE = true
-
-
 --- at class Citation: Element
 --- at field disambiguate_add_givenname boolean?
 --- at field givenname_disambiguation_rule string
@@ -179,7 +176,6 @@
 
 --- at alias CiteId string | number
 
----comment
 --- at param citation_items CitationItem[]
 --- at param engine CiteProc
 --- at param properties CitationProperties
@@ -189,9 +185,6 @@
   local output_format = engine.output_format
   --- at type CiteIr[]
   local irs = {}
-  -- The citation_items are already sorted when evaluate the positions
-  -- To be removed
-  citation_items = self:sorted_citation_items(citation_items, engine)
   for _, cite_item in ipairs(citation_items) do
     local ir = self:build_fully_disambiguated_ir(cite_item, output_format, engine, properties)
     table.insert(irs, ir)
@@ -217,20 +210,20 @@
 
   -- Capitalize first
   for i, ir in ipairs(irs) do
-      -- local layout_prefix
-      -- local layout_affixes = self.layout.affixes
-      -- if layout_affixes then
-      --   layout_prefix = layout_affixes.prefix
-      -- end
+    -- local layout_prefix
+    -- local layout_affixes = self.layout.affixes
+    -- if layout_affixes then
+    --   layout_prefix = layout_affixes.prefix
+    -- end
     local prefix_inlines = ir.cite_prefix
     if prefix_inlines then
       -- Prefix is inlines
       local prefix_str = output.SortStringFormat:new():output(prefix_inlines, context)
       if (string.match(prefix_str, "[.!?]%s*$")
-          -- position_IbidWithPrefixFullStop.txt
-          -- `Book A. He said “Please work.” Ibid.`
-          or string.match(prefix_str, "[.!?]”%s*$")
-         ) and InlineElement.has_space(prefix_inlines) then
+            -- position_IbidWithPrefixFullStop.txt
+            -- `Book A. He said “Please work.” Ibid.`
+            or string.match(prefix_str, "[.!?]”%s*$")
+          ) and InlineElement.has_space(prefix_inlines) then
         ir:capitalize_first_term()
       end
     else
@@ -241,8 +234,6 @@
     end
   end
 
-  -- util.debug(irs)
-
   local citation_stream = {}
 
   local context = Context:new()
@@ -284,10 +275,8 @@
           table.insert(citation_stream, Micro:new(cite_prefix))
         end
 
-        -- util.debug(ir.cite_item.id)
-        -- util.debug(cite_inlines)
         -- if context.engine.opt.citation_link then
-          cite_inlines = {CiteInline:new(cite_inlines, ir.cite_item)}
+        cite_inlines = {CiteInline:new(cite_inlines, ir.cite_item)}
         -- end
         util.extend(citation_stream, cite_inlines)
 
@@ -298,7 +287,7 @@
             -- affix_WithCommas.txt
             local right_most_str = cite_suffix[#cite_suffix]:get_right_most_string()
             if string.match(right_most_str, "[,.;?]%s*$") then
-              ir.own_delimiter = string.gsub(cite_delimiter,  "^[,.;?]", "")
+              ir.own_delimiter = string.gsub(cite_delimiter, "^[,.;?]", "")
             end
           end
         end
@@ -308,8 +297,6 @@
     end
   end
 
-  -- util.debug(citation_stream)
-
   local has_printed_form = true
   if #citation_items == 0 then
     -- bugreports_AuthorOnlyFail.txt
@@ -437,7 +424,6 @@
 --- at param properties CitationProperties
 --- at return CiteIr
 function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine, properties)
-  -- util.debug(cite_item.id)
   local cite_ir = self:build_ambiguous_ir(cite_item, output_format, engine)
   cite_ir = self:apply_disambiguate_add_givenname(cite_ir, engine)
   cite_ir = self:apply_disambiguate_add_names(cite_ir, engine)
@@ -461,7 +447,6 @@
   return cite_ir
 end
 
----comment
 --- at param cite_item CitationItem
 --- at param output_format OutputFormat
 --- at param engine CiteProc
@@ -528,18 +513,6 @@
       break
     end
   end
-  if DEBUG_DISAMBIGUATE then
-    if ir.is_ambiguous then
-      util.debug("[CLASH]")
-      util.debug(string.format("%s: %s", cite_item.id, disam_str))
-      for ir_index, ir_ in pairs(engine.cite_irs_by_output[disam_str]) do
-        util.debug(string.format("%s: %s", ir_.cite_item.id, ir_.disam_str))
-      end
-    else
-      util.debug("[clear]")
-      util.debug(string.format("%s: %s", cite_item.id, disam_str))
-    end
-  end
   engine.cite_irs_by_output[disam_str][ir.ir_index] = ir
 
   return ir
@@ -559,10 +532,6 @@
 
 function Citation:apply_disambiguate_add_givenname(cite_ir, engine)
   if self.disambiguate_add_givenname then
-    if DEBUG_DISAMBIGUATE then
-      util.debug("[Method (1)] disambiguate-add-givenname: " .. cite_ir.cite_item.id)
-    end
-
     local gn_disam_rule = self.givenname_disambiguation_rule
     if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then
       cite_ir = self:apply_disambiguate_add_givenname_all_names(cite_ir, engine)
@@ -577,16 +546,12 @@
 
 -- TODO: reorganize this code
 function Citation:apply_disambiguate_add_givenname_all_names(cite_ir, engine)
-  -- util.debug("disambiguate_add_givenname_all_names: " .. cite_ir.cite_item.id)
   if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then
     return cite_ir
   end
 
-  -- util.debug(cite_ir.disam_str)
-
   for _, person_name_ir in ipairs(cite_ir.person_name_irs) do
     local name_output = person_name_ir.name_output
-    -- util.debug(name_output)
 
     if not person_name_ir.person_name_index then
       person_name_ir.person_name_index = #engine.person_names + 1
@@ -610,12 +575,6 @@
       end
     end
 
-    -- util.debug(person_name_ir.name_output)
-    -- util.debug(person_name_ir.full_name)
-    -- util.debug(#ambiguous_name_irs)
-    -- util.debug(person_name_ir.disam_variants_index)
-    -- util.debug(person_name_ir.disam_variants)
-
     while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do
       if #ambiguous_name_irs == 0 then
         break
@@ -635,14 +594,11 @@
         end
       end
 
-      -- util.debug(person_name_ir.name_output)
-
       -- update ambiguous_name_irs and ambiguous_same_output_irs
       ambiguous_name_irs = {}
       ambiguous_same_output_irs = {}
       for _, pn_ir in pairs(engine.person_names_by_output[person_name_ir.name_output]) do
         if pn_ir.full_name ~= person_name_ir.full_name then
-          -- util.debug(pn_ir.full_name .. ": " .. pn_ir.name_output)
           table.insert(ambiguous_name_irs, pn_ir)
         end
         if pn_ir.name_output == person_name_ir.name_output then
@@ -666,7 +622,6 @@
   -- update ambiguous_cite_irs and ambiguous_same_output_irs
   local ambiguous_cite_irs = {}
   for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
-    -- util.debug(ir_.cite_item.id)
     if ir_.cite_item.id ~= cite_ir.cite_item.id then
       table.insert(ambiguous_cite_irs, ir_)
     end
@@ -684,7 +639,6 @@
   end
   local person_name_ir = cite_ir.person_name_irs[1]
   local name_output = person_name_ir.name_output
-  -- util.debug(name_output)
 
   if not person_name_ir.person_name_index then
     person_name_ir.person_name_index = #engine.person_names + 1
@@ -743,20 +697,13 @@
 end
 
 function Citation:apply_disambiguate_add_givenname_by_cite(cite_ir, engine)
-  -- util.debug(cite_ir.is_ambiguous)
   if not cite_ir.is_ambiguous then
     return cite_ir
   end
-  -- util.debug(cite_ir)
   if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then
     return cite_ir
   end
 
-  -- for _, ir_ in ipairs(engine.disam_irs) do
-  --   util.debug(ir_.cite_item.id)
-  --   util.debug(ir_.disam_str)
-  -- end
-
   local disam_format = DisamStringFormat:new()
 
   local ambiguous_cite_irs = {}
@@ -772,34 +719,27 @@
   end
 
   for i, person_name_ir in ipairs(cite_ir.person_name_irs) do
-    -- util.debug(person_name_ir.name_output)
-    -- util.debug(person_name_ir.disam_variants)
     if #ambiguous_cite_irs == 0 then
       cite_ir.is_ambiguous = false
       break
     end
 
-    -- util.debug(person_name_ir.disam_variants)
     while person_name_ir.disam_variants_index < #person_name_ir.disam_variants do
-      -- util.debug(person_name_ir.name_output)
 
       local is_different_name = false
       for _, ir_ in ipairs(ambiguous_cite_irs) do
         if ir_.person_name_irs[i] then
           if ir_.person_name_irs[i].full_name ~= person_name_ir.full_name then
-            -- util.debug(ir_.cite_item.id)
             is_different_name = true
             break
           end
         end
       end
-      -- util.debug(is_different_name)
       if not is_different_name then
         break
       end
 
       for _, ir_ in ipairs(ambiguous_same_output_irs) do
-        -- util.debug(ir_.cite_item.id)
         local person_name_ir_ = ir_.person_name_irs[i]
         if person_name_ir_ then
           if person_name_ir_.disam_variants_index < #person_name_ir_.disam_variants then
@@ -806,7 +746,6 @@
             person_name_ir_.disam_variants_index = person_name_ir_.disam_variants_index + 1
             local disam_variant = person_name_ir_.disam_variants[person_name_ir_.disam_variants_index]
             person_name_ir_.name_output = disam_variant
-            -- util.debug(disam_variant)
             person_name_ir_.inlines = person_name_ir_.disam_inlines[disam_variant]
             -- Update cite ir output
             local inlines = ir_:flatten(disam_format)
@@ -824,7 +763,6 @@
       ambiguous_cite_irs = {}
       ambiguous_same_output_irs = {}
       for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
-        -- util.debug(ir_.cite_item.id)
         if ir_.cite_item.id ~= cite_ir.cite_item.id then
           table.insert(ambiguous_cite_irs, ir_)
         end
@@ -833,8 +771,6 @@
         end
       end
 
-      -- util.debug(#ambiguous_cite_irs)
-
       if #ambiguous_cite_irs == 0 then
         cite_ir.is_ambiguous = false
         return cite_ir
@@ -869,7 +805,7 @@
   if not cite_ir.name_ir then
     cite_ir.name_ir = find_first_name_ir(cite_ir)
   end
-  local name_ir =  cite_ir.name_ir
+  local name_ir = cite_ir.name_ir
 
   if not cite_ir.is_ambiguous then
     return cite_ir
@@ -879,16 +815,6 @@
     return cite_ir
   end
 
-  if DEBUG_DISAMBIGUATE then
-    util.debug("[Method (3)] disambiguate-add-names: " .. cite_ir.cite_item.id)
-  end
-
-  -- if name_ir then
-  --   util.debug(cite_ir.disam_str)
-  --   util.debug(cite_ir.name_ir.full_name_str)
-  --   util.debug(cite_ir.is_ambiguous)
-  -- end
-
   local disam_format = DisamStringFormat:new()
 
   while cite_ir.is_ambiguous do
@@ -907,7 +833,6 @@
       end
     end
 
-    -- util.debug(#ambiguous_same_output_irs)
     if #ambiguous_cite_irs == 0 then
       cite_ir.is_ambiguous = false
       break
@@ -921,7 +846,6 @@
         break
       end
     end
-    -- util.debug(can_be_disambuguated)
     if not can_be_disambuguated then
       break
     end
@@ -929,7 +853,6 @@
     for _, ir_ in ipairs(ambiguous_same_output_irs) do
       local added_person_name_ir = ir_.name_ir.name_inheritance:expand_one_name(ir_.name_ir)
       if added_person_name_ir then
-        -- util.debug("Updated: " .. ir_.cite_item.id)
         table.insert(ir_.person_name_irs, added_person_name_ir)
 
         if not added_person_name_ir.person_name_index then
@@ -945,7 +868,6 @@
         -- Update ir output
         local inlines = ir_:flatten(disam_format)
         local disam_str = disam_format:output(inlines, nil)
-        -- util.debug(disam_str)
         ir_.disam_str = disam_str
         if not engine.cite_irs_by_output[disam_str] then
           engine.cite_irs_by_output[disam_str] = {}
@@ -954,7 +876,6 @@
       end
     end
 
-    -- util.debug("disambiguate_add_givenname")
 
     if self.disambiguate_add_givenname then
       local gn_disam_rule = self.givenname_disambiguation_rule
@@ -967,13 +888,8 @@
 
     cite_ir.is_ambiguous = self:check_ambiguity(cite_ir, engine)
 
-    -- for _, ir_ in ipairs(engine.disam_irs) do
-    --   util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str)
-    -- end
-
   end
 
-
   return cite_ir
 end
 
@@ -993,10 +909,6 @@
 end
 
 function Citation:apply_disambiguate_conditionals(cite_ir, engine)
-  if DEBUG_DISAMBIGUATE then
-    util.debug("[Method (3)] disambiguate with condition testing “true”: " .. cite_ir.cite_item.id)
-  end
-
   cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir)
 
   local disam_format = DisamStringFormat:new()
@@ -1006,9 +918,6 @@
       break
     end
 
-    -- util.debug(cite_ir.cite_item.id)
-    -- util.debug(cite_ir.disam_str)
-
     -- update ambiguous_same_output_irs
     local ambiguous_same_output_irs = {}
     for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
@@ -1042,11 +951,6 @@
     end
 
     cite_ir.is_ambiguous = self:check_ambiguity(cite_ir, engine)
-    -- util.debug(cite_ir.is_ambiguous)
-    -- for _, ir_ in ipairs(engine.disam_irs) do
-    --   util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str)
-    -- end
-
   end
   return cite_ir
 end
@@ -1058,20 +962,6 @@
       is_ambiguous = true
     end
   end
-  if DEBUG_DISAMBIGUATE then
-    if is_ambiguous then
-      util.debug("[CLASH]")
-      util.debug(string.format("%s, %s", cite_ir.cite_item.id, cite_ir.disam_str))
-      for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
-        if ir_.cite_item.id ~= cite_ir.cite_item.id then
-          util.debug(string.format("%s: %s", ir_.cite_item.id, ir_.disam_str))
-        end
-      end
-    else
-      util.debug("[clear]")
-      util.debug(string.format("%s, %s", cite_ir.cite_item.id, cite_ir.disam_str))
-    end
-  end
   return is_ambiguous
 end
 
@@ -1101,10 +991,6 @@
     return cite_ir
   end
 
-  if DEBUG_DISAMBIGUATE then
-    util.debug("[Method (4)] disambiguate-add-year-suffix”: " .. cite_ir.cite_item.id)
-  end
-
   local ambiguous_same_output_irs = self:get_ambiguous_same_output_cite_irs(cite_ir, engine)
 
   table.sort(ambiguous_same_output_irs, function (a, b)
@@ -1126,7 +1012,6 @@
       ir_.reference.year_suffix_number = year_suffix_number
       ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number)
     end
-    -- util.debug(string.format('%s: %s "%s"', ir_.cite_item.id, tostring(ir_.reference.year_suffix_number), ir_.reference["year-suffix"]))
 
     if not ir_.year_suffix_irs then
       ir_.year_suffix_irs = ir_:collect_year_suffix_irs()
@@ -1134,7 +1019,6 @@
         -- The style does not have a "year-suffix" variable.
         -- Then the year-suffix is appended the first year rendered through cs:date or citation-label
         local year_ir = ir_:find_first_year_ir()
-        -- util.debug(year_ir)
         if year_ir then
           local year_suffix_ir = YearSuffix:new({}, self)
           table.insert(year_ir.children, year_suffix_ir)
@@ -1147,8 +1031,7 @@
 
     -- Render all the year-suffix irs.
     for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do
-      -- util.debug(ir_.reference["year-suffix"])
-      year_suffix_ir.inlines = { PlainText:new(ir_.reference["year-suffix"]) }
+      year_suffix_ir.inlines = {PlainText:new(ir_.reference["year-suffix"])}
       year_suffix_ir.year_suffix_number = ir_.reference.year_suffix_number
       year_suffix_ir.group_var = GroupVar.Important
     end
@@ -1156,7 +1039,6 @@
     -- DisamStringFormat doesn't render YearSuffix and this can be skipped.
     -- local inlines = ir_:flatten(disam_format)
     -- local disam_str = disam_format:output(inlines, nil)
-    -- util.debug(string.format('update %s: "%s" to "%s"', ir_.cite_item.id, ir_.disam_str, disam_str))
     -- ir_.disam_str = disam_str
     -- if not engine.cite_irs_by_output[disam_str] then
     --   engine.cite_irs_by_output[disam_str] = {}
@@ -1179,7 +1061,6 @@
     year_suffix = string.char(i + 97) .. year_suffix
     year_suffix_number = (year_suffix_number - 1) // 26
   end
-  -- util.debug(year_suffix)
   return year_suffix
 end
 
@@ -1206,11 +1087,11 @@
 
   local first_rendering_ir = find_first(ir, function (ir_)
     return (ir_._element_name == "text"
-      or ir_._element_name == "date"
-      or ir_._element_name == "number"
-      or ir_._element_name == "names"
-      or ir_._element_name == "label")
-      and ir_.group_var ~= GroupVar.Missing
+          or ir_._element_name == "date"
+          or ir_._element_name == "number"
+          or ir_._element_name == "names"
+          or ir_._element_name == "label")
+        and ir_.group_var ~= GroupVar.Missing
   end)
   local first_names_ir
   if first_rendering_ir and first_rendering_ir._element_name == "names" then
@@ -1291,11 +1172,11 @@
 function Citation:_apply_cite_author_only(ir)
   local author_ir = find_first(ir, function (ir_)
     return (ir_._element_name == "text"
-      or ir_._element_name == "date"
-      or ir_._element_name == "number"
-      or ir_._element_name == "names"
-      or ir_._element_name == "label")
-      and ir_.group_var ~= GroupVar.Missing
+          or ir_._element_name == "date"
+          or ir_._element_name == "number"
+          or ir_._element_name == "names"
+          or ir_._element_name == "label")
+        and ir_.group_var ~= GroupVar.Missing
   end)
 
   if author_ir then
@@ -1310,7 +1191,6 @@
 function Citation:_apply_suppress_author(ir)
   local author_ir = find_first_names_ir(ir)
   if author_ir then
-    -- util.debug(author_ir)
     author_ir.collapse_suppressed = true
   end
   return ir
@@ -1321,7 +1201,6 @@
 
   local first_names_ir = find_first_names_ir(ir)
   if first_names_ir and engine.style.class ~= "note" then
-    -- util.debug(first_names_ir)
     first_names_ir.collapse_suppressed = true
   end
 
@@ -1416,7 +1295,7 @@
     if i == 1 then
       table.insert(current_group, ir)
     elseif citation_number and previous_citation_number and
-      previous_citation_number + 1 == citation_number then
+        previous_citation_number + 1 == citation_number then
       table.insert(current_group, ir)
     else
       table.insert(cite_groups, current_group)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-date.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -25,7 +25,6 @@
 end
 
 local Element = element.Element
-local IrNode = ir_node.IrNode
 local Rendered = ir_node.Rendered
 local SeqIr = ir_node.SeqIr
 local GroupVar = ir_node.GroupVar
@@ -243,7 +242,7 @@
       else
         if #range_part_queue > 0 then
           table.insert(irs, self:build_date_range_parts(range_part_queue, variable,
-              delimiter, engine, state, context, range_delimiter))
+            delimiter, engine, state, context, range_delimiter))
           range_part_queue = {}
         end
         table.insert(irs, date_part:build_ir(first, engine, state, context))
@@ -252,7 +251,7 @@
   end
   if #range_part_queue > 0 then
     table.insert(irs, self:build_date_range_parts(range_part_queue, variable,
-    delimiter, engine, state, context, range_delimiter))
+      delimiter, engine, state, context, range_delimiter))
   end
 
   local ir = SeqIr:new(irs, self)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-group.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-group.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-group.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -21,7 +21,6 @@
   util = require("citeproc.util")
 end
 
-local SeqIr = ir_node.SeqIr
 local Element = element.Element
 
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-label.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -8,7 +8,6 @@
 
 local element
 local ir_node
-local output
 local util
 
 local using_luatex, kpse = pcall(require, "kpse")
@@ -15,19 +14,15 @@
 if using_luatex then
   element = require("citeproc-element")
   ir_node = require("citeproc-ir-node")
-  output = require("citeproc-output")
   util = require("citeproc-util")
 else
   element = require("citeproc.element")
   ir_node = require("citeproc.ir-node")
-  output = require("citeproc.output")
   util = require("citeproc.util")
 end
 
 local Element = element.Element
-local IrNode = ir_node.IrNode
 local Rendered = ir_node.Rendered
-local PlainText = output.PlainText
 
 
 -- [Label](https://docs.citationstyles.org/en/stable/specification.html#label)
@@ -55,6 +50,10 @@
   return o
 end
 
+--- at param engine CiteProc
+--- at param state table
+--- at param context Context
+--- at return Rendered?
 function Label:build_ir(engine, state, context)
   -- local variable = context:get_variable(self.variable, self.form)
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-layout.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-layout.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-layout.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -77,7 +77,6 @@
       ir.formatting = util.clone(self.formatting)
     end
   end
-  -- util.debug(ir)
   return ir
 end
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-locale.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -100,10 +100,10 @@
 
 Locale.form_fallbacks = {
   ["verb-short"] = {"verb-short", "verb", "long"},
-  ["verb"]       = {"verb", "long"},
-  ["symbol"]     = {"symbol", "short", "long"},
-  ["short"]      = {"short", "long"},
-  ["long"]       = {"long"},
+  ["verb"] = {"verb", "long"},
+  ["symbol"] = {"symbol", "short", "long"},
+  ["short"] = {"short", "long"},
+  ["long"] = {"long"},
 }
 
 
@@ -205,7 +205,6 @@
   o:process_children_nodes(node)
   for _, term in ipairs(o.children) do
     local form = term.form
-    local gender = term.gender
     local gender_form = term.gender_form
     local match
     if util.startswith(term.name, "ordinal-0") then
@@ -215,17 +214,17 @@
     end
 
     local key = term.name
-    if form then
-      key = key .. '/form-' .. form
+    if form and form ~= "long" then
+      key = key .. "/form-" .. form
     end
     -- if gender then
     --   key = key .. '/gender-' .. gender
     -- end
     if gender_form then
-      key = key .. '/gender-form-' .. gender_form
+      key = key .. "/gender-form-" .. gender_form
     end
     if match then
-      key = key .. '/match-' .. match
+      key = key .. "/match-" .. match
     end
 
     if term.name == "ordinal" or util.startswith(term.name, "ordinal-") then

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-names.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -27,7 +27,6 @@
   util = require("citeproc.util")
 end
 
-local IrNode = ir_node.IrNode
 local NameIr = ir_node.NameIr
 local PersonNameIr = ir_node.PersonNameIr
 local SeqIr = ir_node.SeqIr
@@ -201,7 +200,6 @@
 
   local irs = {}
   local num_names = 0
-  -- util.debug(self.name)
 
   local index_by_variable = {}
 
@@ -209,7 +207,8 @@
   -- substitute_SubstituteOnlyOnceString.txt
   if names_inheritance.variable then
     for _, variable in ipairs(util.split(names_inheritance.variable)) do
-      local name_ir = names_inheritance.name:build_ir(variable, names_inheritance.et_al, names_inheritance.label, engine, state, context)
+      local name_ir = names_inheritance.name:build_ir(variable, names_inheritance.et_al, names_inheritance.label,
+        engine, state, context)
       if name_ir and names_inheritance.name.form == "count" then
         num_names = num_names + name_ir.name_count
       end
@@ -245,7 +244,8 @@
 
     if editor_name_ir.full_name_str == translator_name_ir.full_name_str then
       local names = context:get_variable("editor")
-      local editor_translator_label_ir = names_inheritance.name:build_name_label(names_inheritance.label, "editortranslator", names, context)
+      local editor_translator_label_ir = names_inheritance.name:build_name_label(names_inheritance.label,
+        "editortranslator", names, context)
       if editor_translator_label_ir then
         local first_index = index_by_variable.editor
         local second_index = index_by_variable.translator
@@ -273,7 +273,6 @@
       ir = NameIr:new({ir}, self)
       ir.name_count = num_names
       ir.group_var = GroupVar.Important
-      -- util.debug(ir)
       return ir
     end
   else
@@ -326,7 +325,7 @@
 
 function Names:substitute_names(result, context)
   if not context.build.first_rendered_names then
-     return result
+    return result
   end
   local name_strings = {}
   local match_all
@@ -477,7 +476,8 @@
     label = nil
   end
 
-  local et_al_abbreviation = self.et_al_min and self.et_al_use_first and #names >= self.et_al_min and #names > self.et_al_use_first
+  local et_al_abbreviation = self.et_al_min and self.et_al_use_first and #names >= self.et_al_min and
+      #names > self.et_al_use_first
   local use_last = et_al_abbreviation and self.et_al_use_last and self.et_al_use_first <= self.et_al_min - 2
 
   if self.form == "count" then
@@ -656,8 +656,6 @@
     (is_sort and context.style.demote_non_dropping_particle == "sort-only"))
 
   local name_part_tokens = self:get_display_order(name, self.form, is_latin, is_sort, is_inverted, demote_ndp)
-  -- util.debug(name)
-  -- util.debug(name_part_tokens)
 
   local inlines = {}
   for i, token in ipairs(name_part_tokens) do
@@ -678,7 +676,7 @@
       util.extend(inlines, InlineElement:parse(text, context))
 
     elseif token == "literal" then
-    local literal_inlines = self.family:format_text_case(name.literal, context)
+      local literal_inlines = self.family:format_text_case(name.literal, context)
       util.extend(inlines, literal_inlines)
 
     elseif token == "space" then
@@ -691,7 +689,6 @@
       table.insert(inlines, PlainText:new(self.sort_separator))
     end
   end
-  -- util.debug(inlines)
   return inlines
 end
 
@@ -991,7 +988,7 @@
 
     local first_letter = utf8.char(utf8.codepoint(name))
     if unicode.islower(first_letter) then
-        is_particle = true
+      is_particle = true
     elseif #name == 1 then
       is_abbreviation = true
     else
@@ -1004,8 +1001,8 @@
 
     if is_particle then
       name_list[i] = name .. " "
-      if i > 1 and not string.match(name_list[i-1], "%s$") then
-        name_list[i-1] = name_list[i-1] .. " "
+      if i > 1 and not string.match(name_list[i - 1], "%s$") then
+        name_list[i - 1] = name_list[i - 1] .. " "
       end
     elseif is_abbreviation then
       name_list[i] = name .. with
@@ -1037,11 +1034,11 @@
     end
 
     -- Handle the compound names
-    if i > 1 and punct_list[i-1] == "-" then
+    if i > 1 and punct_list[i - 1] == "-" then
       if is_particle then  -- special case "Guo-ping"
         name_list[i] = ""
       else
-        name_list[i-1] = util.rstrip(name_list[i-1])
+        name_list[i - 1] = util.rstrip(name_list[i - 1])
         name_list[i] = "-" .. name_list[i]
       end
     end
@@ -1124,7 +1121,7 @@
   table.remove(hidden_name_irs, 1)
   if #hidden_name_irs == 0 then
     if name_ir.et_al_abbreviation then
-      name_ir.et_al_abbreviation  = false
+      name_ir.et_al_abbreviation = false
     end
     if name_ir.use_last then
       name_ir.use_last = false

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-number.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-number.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-number.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -8,17 +8,14 @@
 
 local element
 local ir_node
-local util
 
 local using_luatex, kpse = pcall(require, "kpse")
 if using_luatex then
   element = require("citeproc-element")
   ir_node = require("citeproc-ir-node")
-  util = require("citeproc-util")
 else
   element = require("citeproc.element")
   ir_node = require("citeproc.ir-node")
-  util = require("citeproc.util")
 end
 
 local Element = element.Element
@@ -35,7 +32,7 @@
 --- at field text_case string?
 local Number = Element:derive("number")
 
-function Number:new(node)
+function Number:new()
   local o = {
     element_name = "number",
     form = "numeric",

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-sort.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-sort.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-sort.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -106,12 +106,9 @@
     table.insert(key_map[item.id], i)
   end
 
-  -- util.debug(key_map)
-
   local function compare_entry(item1, item2)
     return self.compare_entry(key_map, sort_directions, item1, item2)
   end
-  -- util.debug(items)
   table.sort(items, compare_entry)
 
   return items
@@ -215,7 +212,6 @@
     end
     local output_format = context.format
     local inlines = ir:flatten(output_format)
-    -- util.debug(inlines)
     local str = output_format:output(inlines)
     return str
   end

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-style.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -8,8 +8,6 @@
 
 local dom
 local element
-local ir_node
-local output
 local node_names
 local util
 
@@ -17,25 +15,16 @@
 if using_luatex then
   dom = require("luaxml-domobject")
   element = require("citeproc-element")
-  ir_node = require("citeproc-ir-node")
-  output = require("citeproc-output")
   node_names = require("citeproc-node-names")
   util = require("citeproc-util")
 else
   dom = require("citeproc.luaxml.domobject")
   element = require("citeproc.element")
-  ir_node = require("citeproc.ir-node")
-  output = require("citeproc.output")
   node_names = require("citeproc.node-names")
   util = require("citeproc.util")
 end
 
 local Element = element.Element
-local IrNode = ir_node.IrNode
-local Rendered = ir_node.Rendered
-local SeqIr = ir_node.SeqIr
-local PlainText = output.PlainText
-local DisamStringFormat = output.DisamStringFormat
 
 
 --- at class Style: Element
@@ -285,7 +274,6 @@
 end
 
 
-
 style_module.Style = Style
 
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-node-text.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -87,7 +87,6 @@
   if self.variable then
     ir = self:build_variable_ir(engine, state, context)
   elseif self.macro then
-    -- util.debug(self.macro)
     ir = self:build_macro_ir(engine, state, context)
   elseif self.term then
     ir = self:build_term_ir(engine, state, context)
@@ -236,7 +235,6 @@
     util.error(string.format("Macro '%s' not found", self.macro))
     return nil
   end
-  -- util.debug(string.format('<macro name="%s">', self.macro))
   state:push_macro(self.macro)
   local ir = macro:build_ir(engine, state, context)
   state:pop_macro(self.macro)
@@ -248,7 +246,6 @@
       ir.quotes = context:get_localized_quotes()
     end
   end
-  -- util.debug(ir)
   return ir
 end
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-nodes.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -20,60 +20,60 @@
 
 local using_luatex, kpse = pcall(require, "kpse")
 if using_luatex then
-  style  = require("citeproc-node-style")
-  citation  = require("citeproc-node-citation")
-  bibliography  = require("citeproc-node-bibliography")
+  style = require("citeproc-node-style")
+  citation = require("citeproc-node-citation")
+  bibliography = require("citeproc-node-bibliography")
   locale = require("citeproc-node-locale")
   layout = require("citeproc-node-layout")
-  text   = require("citeproc-node-text")
-  date   = require("citeproc-node-date")
+  text = require("citeproc-node-text")
+  date = require("citeproc-node-date")
   number = require("citeproc-node-number")
-  names  = require("citeproc-node-names")
-  label  = require("citeproc-node-label")
-  group  = require("citeproc-node-group")
+  names = require("citeproc-node-names")
+  label = require("citeproc-node-label")
+  group = require("citeproc-node-group")
   choose = require("citeproc-node-choose")
-  sort   = require("citeproc-node-sort")
+  sort = require("citeproc-node-sort")
 else
-  style  = require("citeproc.node-style")
-  citation  = require("citeproc.node-citation")
-  bibliography  = require("citeproc.node-bibliography")
+  style = require("citeproc.node-style")
+  citation = require("citeproc.node-citation")
+  bibliography = require("citeproc.node-bibliography")
   locale = require("citeproc.node-locale")
   layout = require("citeproc.node-layout")
-  text   = require("citeproc.node-text")
-  date   = require("citeproc.node-date")
+  text = require("citeproc.node-text")
+  date = require("citeproc.node-date")
   number = require("citeproc.node-number")
-  names  = require("citeproc.node-names")
-  label  = require("citeproc.node-label")
-  group  = require("citeproc.node-group")
+  names = require("citeproc.node-names")
+  label = require("citeproc.node-label")
+  group = require("citeproc.node-group")
   choose = require("citeproc.node-choose")
-  sort   = require("citeproc.node-sort")
+  sort = require("citeproc.node-sort")
 end
 
 local nodes = {
-  ["style"]        = style.Style,
-  ["citation"]     = citation.Citation,
-  ["intext"]       = citation.Intext,
+  ["style"] = style.Style,
+  ["citation"] = citation.Citation,
+  ["intext"] = citation.Intext,
   ["bibliography"] = bibliography.Bibliography,
-  ["locale"]       = locale.Locale,
-  ["term"]         = locale.Term,
-  ["layout"]       = layout.Layout,
-  ["text"]         = text.Text,
-  ["date"]         = date.Date,
-  ["date-part"]    = date.DatePart,
-  ["number"]       = number.Number,
-  ["names"]        = names.Names,
-  ["name"]         = names.Name,
-  ["name-part"]    = names.NamePart,
-  ["et-al"]        = names.EtAl,
-  ["substitute"]   = names.Substitute,
-  ["label"]        = label.Label,
-  ["group"]        = group.Group,
-  ["choose"]       = choose.Choose,
-  ["if"]           = choose.If,
-  ["else"]         = choose.Else,
-  ["else-if"]      = choose.ElseIf,
-  ["sort"]         = sort.Sort,
-  ["key"]          = sort.Key,
+  ["locale"] = locale.Locale,
+  ["term"] = locale.Term,
+  ["layout"] = layout.Layout,
+  ["text"] = text.Text,
+  ["date"] = date.Date,
+  ["date-part"] = date.DatePart,
+  ["number"] = number.Number,
+  ["names"] = names.Names,
+  ["name"] = names.Name,
+  ["name-part"] = names.NamePart,
+  ["et-al"] = names.EtAl,
+  ["substitute"] = names.Substitute,
+  ["label"] = label.Label,
+  ["group"] = group.Group,
+  ["choose"] = choose.Choose,
+  ["if"] = choose.If,
+  ["else"] = choose.Else,
+  ["else-if"] = choose.ElseIf,
+  ["sort"] = sort.Sort,
+  ["key"] = sort.Key,
 }
 
 return nodes

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-output.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -9,21 +9,18 @@
 local lpeg = require("lpeg")
 local uni_utf8
 local unicode
-local dom
 local ir_node
 local util
 
-local using_luatex, kpse = pcall(require, "kpse")
+local using_luatex, _ = pcall(require, "kpse")
 if using_luatex then
   uni_utf8 = require("unicode").utf8
   unicode = require("citeproc-unicode")
-  dom = require("luaxml-domobject")
   ir_node = require("citeproc-ir-node")
   util = require("citeproc-util")
 else
   uni_utf8 = require("lua-utf8")
   unicode = require("citeproc.unicode")
-  dom = require("citeproc.luaxml.domobject")
   ir_node = require("citeproc.ir-node")
   util = require("citeproc.util")
 end
@@ -33,19 +30,19 @@
 
 --- at class LocalizedQuotes
 local LocalizedQuotes = {
-  outer_open = util.unicode['left double quotation mark'],
-  outer_close = util.unicode['right double quotation mark'],
-  inner_open = util.unicode['left single quotation mark'],
-  inner_close = util.unicode['right single quotation mark'],
+  outer_open = util.unicode["left double quotation mark"],
+  outer_close = util.unicode["right double quotation mark"],
+  inner_open = util.unicode["left single quotation mark"],
+  inner_close = util.unicode["right single quotation mark"],
   punctuation_in_quote = false,
 }
 
 function LocalizedQuotes:new(outer_open, outer_close, inner_open, inner_close, punctuation_in_quote)
   local o = {
-    outer_open = outer_open or util.unicode['left double quotation mark'],
-    outer_close = outer_close or util.unicode['right double quotation mark'],
-    inner_open = inner_open or util.unicode['left single quotation mark'],
-    inner_close = inner_close or util.unicode['right single quotation mark'],
+    outer_open = outer_open or util.unicode["left double quotation mark"],
+    outer_close = outer_close or util.unicode["right double quotation mark"],
+    inner_open = inner_open or util.unicode["left single quotation mark"],
+    inner_close = inner_close or util.unicode["right single quotation mark"],
     punctuation_in_quote = punctuation_in_quote,
   }
   setmetatable(o, self)
@@ -69,13 +66,11 @@
 }
 
 
-
----comment
 --- at param class_name string
 --- at return table
 function InlineElement:derive(class_name)
   local o = {
-    _type  = class_name,
+    _type = class_name,
   }
   -- self[class_name] = o
   setmetatable(o, self)
@@ -87,7 +82,7 @@
 function InlineElement:new(inlines)
   local o = {
     inlines = inlines,
-    _type  = self._type,
+    _type = self._type,
   }
   setmetatable(o, self)
   return o
@@ -322,7 +317,6 @@
 end
 
 
-
 --- at param text string
 --- at param context Context?
 --- at param is_external boolean?
@@ -335,9 +329,7 @@
     inlines = self:parse_csl_rich_text(text)
   elseif text_type == "string" then
     -- String with HTML-like formatting tags
-    -- util.debug(text)
     inlines = self:parse_html_tags(text, context, is_external)
-    -- util.debug(inlines)
   elseif text_type == "number" then
     inlines = {PlainText:new(tostring(text))}
   else
@@ -434,11 +426,11 @@
 local basic_tag_pattern = P '<span class="nocase">'
     + P '<span class="nodecor">'
     + P '<span style="font-variant:small-caps;">'
-    + P '<sc>'
-    + P '<i>'
-    + P '<b>'
-    + P '<sup>'
-    + P '<sub>'
+    + P "<sc>"
+    + P "<i>"
+    + P "<b>"
+    + P "<sup>"
+    + P "<sub>"
     + P ' "'
     + P " '"
     + P '("'
@@ -445,12 +437,12 @@
     + P "('"
     + P "“"
     + P "‘"
-    + P '</span>'
-    + P '</sc>'
-    + P '</i>'
-    + P '</b>'
-    + P '</sup>'
-    + P '</sub>'
+    + P "</span>"
+    + P "</sc>"
+    + P "</i>"
+    + P "</b>"
+    + P "</sup>"
+    + P "</sub>"
     + P '"'
     + P "'"
     + P "”"
@@ -471,23 +463,23 @@
     closer = "</span>",
     quotes = false,
   },
-  ['<sc>'] = {
+  ["<sc>"] = {
     closer = "</sc>",
     quotes = false,
   },
-  ['<i>'] = {
+  ["<i>"] = {
     closer = "</i>",
     quotes = false,
   },
-  ['<b>'] = {
+  ["<b>"] = {
     closer = "</b>",
     quotes = false,
   },
-  ['<sup>'] = {
+  ["<sup>"] = {
     closer = "</sup>",
     quotes = false,
   },
-  ['<sub>'] = {
+  ["<sub>"] = {
     closer = "</sub>",
     quotes = false,
   },
@@ -507,23 +499,23 @@
     closer = "’",
     quotes = true,
   },
-  ['<code>'] = {
+  ["<code>"] = {
     closer = "</code>",
     quotes = false,
   },
-  ['<script>'] = {
+  ["<script>"] = {
     closer = "</script>",
     quotes = false,
   },
-  ['<pre>'] = {
+  ["<pre>"] = {
     closer = "</pre>",
     quotes = false,
   },
-  ['<math>'] = {
+  ["<math>"] = {
     closer = "</math>",
     quotes = false,
   },
-  ['<math-tex>'] = {
+  ["<math-tex>"] = {
     closer = "</math-tex>",
     quotes = false,
   },
@@ -552,7 +544,7 @@
     openers_info[localed_quotes.outer_open] = {
       closer = localed_quotes.outer_close,
       quotes = true,
-      inner = false
+      inner = false,
     }
   end
 
@@ -563,7 +555,7 @@
     openers_info[localed_quotes.inner_open] = {
       closer = localed_quotes.inner_close,
       quotes = true,
-      inner = true
+      inner = true,
     }
   end
 
@@ -600,7 +592,7 @@
 
   local tag_positions_list = lpeg.match(tag_pattern, str)
   if not tag_positions_list then
-    error('Pattern not match')
+    error("Pattern not match")
   end
   local start = 1
   local stop = 1
@@ -664,15 +656,15 @@
     return NoDecor:new(inlines)
   elseif tag == '<span style="font-variant:small-caps;">' then
     return Formatted:new(inlines, {["font-variant"] = "small-caps"})
-  elseif tag == '<sc>' then
+  elseif tag == "<sc>" then
     return Formatted:new(inlines, {["font-variant"] = "small-caps"})
-  elseif tag == '<i>' then
+  elseif tag == "<i>" then
     return Formatted:new(inlines, {["font-style"] = "italic"})
-  elseif tag == '<b>' then
+  elseif tag == "<b>" then
     return Formatted:new(inlines, {["font-weight"] = "bold"})
-  elseif tag == '<sup>' then
+  elseif tag == "<sup>" then
     return Formatted:new(inlines, {["vertical-align"] = "sup"})
-  elseif tag == '<sub>' then
+  elseif tag == "<sub>" then
     return Formatted:new(inlines, {["vertical-align"] = "sub"})
   elseif openers_info[tag] and openers_info[tag].quotes then
     local localized_quotes = context:get_localized_quotes()
@@ -714,9 +706,6 @@
 
   local tags, strings = split_tags_and_strings(str, context)
 
-  -- util.debug(tags)
-  -- util.debug(strings)
-
   -- if #tags == 0 then
   --   return {PlainText:new(str)}
   -- end
@@ -725,10 +714,6 @@
   local opener_stack = {}
 
   for i, tag in ipairs(tags) do
-    -- util.debug(i)
-    -- util.debug(strings[i])
-    -- util.debug(tag)
-    -- util.debug(opener_stack)
     local apostrophe = _apostrophe_force(tag, strings[i + 1])
     if apostrophe then
       strings[i + 1] = apostrophe .. strings[i + 1]
@@ -785,8 +770,6 @@
     end
   end
 
-  -- util.debug(opener_stack)
-
   -- Process remainders in the stack
   if #opener_stack > 0 then
     for _, opener_info in ipairs(opener_stack) do
@@ -810,8 +793,6 @@
 
   -- The first appearance of straight quote is treated as outer quote.
   for _, tag in ipairs(tags) do
-    -- util.debug(i)
-    -- util.debug(tag)
     if straight_quotes_flip[tag] then
       set_outer_quote_form(tag, openers_info)
       break
@@ -818,9 +799,6 @@
     end
   end
 
-  -- util.debug(tags)
-  -- util.debug(strings)
-
   --- at type InlineElement[]
   local output = {}
 
@@ -837,7 +815,7 @@
         tag = tag,
         closer = openers_info[tag].closer,
         pos = i,
-        output_pos = #output + 1
+        output_pos = #output + 1,
       })
     else
       assert(#opener_stack > 0)
@@ -864,17 +842,16 @@
 
   end
 
-  -- util.debug(output)
   return output
 end
 
 local function merge_fragments_at(fragments, i)
-  if type(fragments[i+1]) == "string" then
-    fragments[i] = fragments[i] .. fragments[i+1]
-    table.remove(fragments, i+1)
+  if type(fragments[i + 1]) == "string" then
+    fragments[i] = fragments[i] .. fragments[i + 1]
+    table.remove(fragments, i + 1)
   end
-  if type(fragments[i-1]) == "string" then
-    fragments[i-1] = fragments[i-1] .. fragments[i]
+  if type(fragments[i - 1]) == "string" then
+    fragments[i - 1] = fragments[i - 1] .. fragments[i]
     table.remove(fragments, i)
   end
 end
@@ -905,7 +882,7 @@
       local start_idx = 1
       for _, quote_tuple in ipairs(quote_tuples) do
         local idx, qt, next_idx = table.unpack(quote_tuple)
-        local fragment = string.sub(inline.value, start_idx, idx-1)
+        local fragment = string.sub(inline.value, start_idx, idx - 1)
         if fragment ~= "" then
           table.insert(fragments, fragment)
         end
@@ -942,13 +919,13 @@
           if string.match(left, "%s$") and string.match(right, "^%s") then
             -- Orphan quote: bugreports_SingleQuote.txt
             if fragment == "'" then
-              fragments[i] = util.unicode['apostrophe']
+              fragments[i] = util.unicode["apostrophe"]
             end
             merge_fragments_at(fragments, i)
           end
           if not string.match(left, "[%s%p]$") and not string.match(right, "^[%s%p]") then
             if fragment == "'" then
-              fragments[i] = util.unicode['apostrophe']
+              fragments[i] = util.unicode["apostrophe"]
             end
             merge_fragments_at(fragments, i)
           end
@@ -976,12 +953,11 @@
 end
 
 function InlineElement:capitalize_first_term()
-  -- util.debug(self)
   if self._type == "PlainText" then
     self.value = unicode.capitalize(self.value)
   elseif self._type ~= "Code" and self._type ~= "MathML" and self._type ~= "MathTeX" then
     if self.inlines[1] then
-    self.inlines[1]:capitalize_first_term()
+      self.inlines[1]:capitalize_first_term()
     end
   end
 end
@@ -1109,7 +1085,6 @@
   local words = unicode.words(str)
   for _, word in ipairs(words) do
     if unicode.islower(word) and not util.stop_words[word] then
-      -- util.debug(word)
       return true
     end
   end
@@ -1123,7 +1098,6 @@
   for _, inline in ipairs(inlines) do
     if util.is_instance(inline, PlainText) then
       if is_str_sentence_case(inline.value) then
-        -- util.debug(inline.value)
         return true
       end
 
@@ -1133,9 +1107,9 @@
         or util.is_instance(inline, CiteInline)
         or util.is_instance(inline, UndefinedCite)
         or (util.is_instance(inline, Formatted)
-            and inline.formatting["font-variant"] ~= "small-caps"
-            and inline.formatting["vertical-align"] ~= "sup"
-            and inline.formatting["vertical-align"] ~= "sub") then
+          and inline.formatting["font-variant"] ~= "small-caps"
+          and inline.formatting["vertical-align"] ~= "sup"
+          and inline.formatting["vertical-align"] ~= "sub") then
       if self:is_sentence_case(inline.inlines) then
         return true
       end
@@ -1152,12 +1126,10 @@
 --- at param inlines table[]
 --- at param check_sentence_case boolean
 function OutputFormat:convert_sentence_case(inlines, check_sentence_case)
-  if not inlines or #inlines == 0  then
+  if not inlines or #inlines == 0 then
     return
   end
   local is_uppercase = false  -- TODO
-  -- util.debug(check_sentence_case)
-  -- util.debug(self:is_sentence_case(inlines))
   if check_sentence_case then
     if self:is_sentence_case(inlines) then
       self:apply_text_case_inner(inlines, "capitalize-first", false, is_uppercase)
@@ -1205,24 +1177,21 @@
     end
     local is_last = (i == #inlines)
     if inline._type == "PlainText" then
-      -- util.debug(inline.value)
-      -- util.debug(text_case)
       inline.value = self:transform_case(inline.value, text_case, seen_one, is_last, is_uppercase);
-      -- util.debug(inline.value)
       seen_one = seen_one or string_contains_word(inline.value)
 
     elseif inline._type == "NoCase" or
-           inline._type == "NoDecor" or
-           inline._type == "Code" or
-           inline._type == "MathML" or
-           inline._type == "MathTeX" or
-           (inline._type == "Formatted" and inline.formatting["font-variant"] == "small-caps") or
-           (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sup") or
-           (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sub") then
+        inline._type == "NoDecor" or
+        inline._type == "Code" or
+        inline._type == "MathML" or
+        inline._type == "MathTeX" or
+        (inline._type == "Formatted" and inline.formatting["font-variant"] == "small-caps") or
+        (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sup") or
+        (inline._type == "Formatted" and inline.formatting["vertical-align"] == "sub") then
       seen_one = seen_one or inline_contains_word(inline)
 
     elseif inline._type == "Formatted" or inline._type == "Quoted"
-      or inline._type == "CiteInline" or inline._type == "UndefinedCite" then
+        or inline._type == "CiteInline" or inline._type == "UndefinedCite" then
       seen_one = self:apply_text_case_inner(inline.inlines, text_case, seen_one, is_uppercase) or seen_one
 
     end
@@ -1239,7 +1208,6 @@
   return unicode.upper(str)
 end
 
----comment
 --- at param str string
 --- at param is_first boolean
 --- at param transform function
@@ -1250,7 +1218,6 @@
     for i, segment in ipairs(segments) do
       -- bugreports_ThesisUniversityAppearsTwice.txt: "Ph.D"
       if uni_utf8.match(segment, "%a") then
-        -- util.debug(segment)
         segments[i] = transform(segment)
         break
       end
@@ -1269,7 +1236,6 @@
   Other = 0,
 }
 
----comment
 --- at param str string
 --- at param seen_one boolean
 --- at param is_last_inline boolean
@@ -1277,7 +1243,6 @@
 --- at return string
 local function transform_each_word(str, seen_one, is_last_inline, transform)
   local segments = unicode.split_word_bounds(str)
-  -- util.debug(segments)
 
   local segment_type_list = {}
   local last_word_idx
@@ -1291,10 +1256,11 @@
 
     elseif uni_utf8.match(segment, "%p") then
       segment_type_list[i] = SegmentType.Puctuation
-      if segment == "!" or segment == "?" then
+      -- In the case of `Form ({MMPI-2-RF}): Technical Manual`, use `endswith()`.
+      if util.endswith(segment, "!") or util.endswith(segment, "?") then
         segment_type_list[i] = SegmentType.EndingPunctuation
-      -- elseif segment == ":" or segment == "—" then
-      elseif segment == ":" then
+        -- elseif segment == ":" or segment == "—" then
+      elseif util.endswith(segment, ":") then
         -- Em dash is not taken into consideration, see "Stability—with Job" in `textcase_TitleWithEmDash.txt`
         segment_type_list[i] = SegmentType.Colon
       end
@@ -1307,20 +1273,17 @@
   for i, segment in ipairs(segments) do
     if segment_type_list[i] == SegmentType.Word then
       local is_first_word = sentence_begin
-      local is_last_word = segment_type_list[i+1] == SegmentType.EndingPunctuation
-        or (is_last_inline and i == last_word_idx)
+      local is_last_word = segment_type_list[i + 1] == SegmentType.EndingPunctuation
+          or (is_last_inline and i == last_word_idx)
       -- See "Pro-Environmental" in `textcase_StopWordBeforeHyphen.txt`
       -- but not "Out-of-Fashion" in `textcase_TitleCaseWithHyphens.txt`.
-      local ignore_stop_word = segments[i+1] == "-" and segments[i-1] ~= "-"
+      local ignore_stop_word = segments[i + 1] == "-" and segments[i - 1] ~= "-"
 
-      -- util.debug(segment)
-      -- util.debug(after_colon)
       -- See "07-x" in `textcase_LastChar.txt`
-      if not (segments[i-1] == "-" and unicode.len(segment) == 1) then
+      if not (segments[i - 1] == "-" and unicode.len(segment) == 1) then
         segments[i] = transform(segment, is_first_word, after_colon, is_last_word, ignore_stop_word)
 
       end
-      -- util.debug(segments[i])
 
       sentence_begin = false
       after_colon = false
@@ -1336,7 +1299,6 @@
   return table.concat(segments)
 end
 
----comment
 --- at param word string
 --- at return string
 local function transform_capitalize_word_if_lower(word)
@@ -1352,8 +1314,6 @@
   -- Entirely non-English
   -- e.g. "β" in "β-Carotine"
   -- TODO: two-word cases like "due to"
-  -- util.debug(word)
-  -- util.debug(ignore_stop_word)
   local res
   if (is_first or is_last or after_end_punct or ignore_stop_word or not util.stop_words[word])
       and string.match(word, "%a") and unicode.islower(word) then
@@ -1361,12 +1321,10 @@
   else
     res = word
   end
-  -- util.debug(res)
   return res
 end
 
 local function transform_lowercase_if_capitalize(word, is_first, after_end_punct, is_last, is_stop_word)
-  -- util.debug(word)
   if not (is_first or after_end_punct) then
     local is_capitalize_word = false
     local lower_first = string.gsub(word, utf8.charpattern, unicode.lower, 1)
@@ -1448,8 +1406,6 @@
 
   self:move_punctuation(inlines)
 
-  -- util.debug(inlines)
-
   return self:write_inlines(inlines, context)
 end
 
@@ -1458,7 +1414,6 @@
 --- at return string?
 function OutputFormat:output_bibliography_entry(inlines, context)
   self:flip_flop_inlines(inlines)
-  -- util.debug(inlines)
   self:move_punctuation(inlines)
   -- TODO:
   -- if self.format == "html" then
@@ -1506,9 +1461,9 @@
           if value == state[attribute] then
             if value == "normal" then
               formatting[attribute] = nil
-            -- Formatting outside Micro is not reset to "normal".
-            -- else
-            --   formatting[attribute] = "normal"
+              -- Formatting outside Micro is not reset to "normal".
+              -- else
+              --   formatting[attribute] = "normal"
             end
           end
           new_state[attribute] = value
@@ -1625,9 +1580,9 @@
 local function find_left(inline)
   if inline._type == "PlainText" then
     return inline
-  -- elseif inline._type == "Micro" then
-  --   return nil
-  elseif inline.inlines and #inline.inlines > 0 and inline._type~="Quoted" then
+    -- elseif inline._type == "Micro" then
+    --   return nil
+  elseif inline.inlines and #inline.inlines > 0 and inline._type ~= "Quoted" then
     return find_left(inline.inlines[1])
   else
     return nil
@@ -1637,8 +1592,8 @@
 local function find_right(inline)
   if inline._type == "PlainText" then
     return inline
-  -- elseif inline._type == "Micro" then
-  --   return nil
+    -- elseif inline._type == "Micro" then
+    --   return nil
   elseif inline.inlines and #inline.inlines > 0 and inline._type ~= "Quoted" then
     return find_right(inline.inlines[#inline.inlines])
   else
@@ -1649,8 +1604,8 @@
 local function find_right_in_quoted(inline)
   if inline._type == "PlainText" then
     return inline
-  -- elseif inline._type == "Micro" then
-  --   return nil
+    -- elseif inline._type == "Micro" then
+    --   return nil
   elseif inline.inlines and #inline.inlines > 0 then
     return find_right_in_quoted(inline.inlines[#inline.inlines])
   else
@@ -1665,8 +1620,8 @@
   if inline._type == "Quoted" and #inline.inlines > 0 then
     --- at cast inline Quoted
     return find_right_in_quoted(inline.inlines[#inline.inlines]), inline.quotes.punctuation_in_quote
-  -- elseif inline._type == "Micro" then
-  --   return nil
+    -- elseif inline._type == "Micro" then
+    --   return nil
   elseif inline.inlines and #inline.inlines > 0 then
     return find_right_quoted(inline.inlines[#inline.inlines])
   else
@@ -1677,12 +1632,10 @@
 local function smash_string_push(first, second)
   local first_char = string.sub(first.value, -1)
   local second_char = string.sub(second.value, 1, 1)
-  -- util.debug(first_char)
-  -- util.debug(second_char)
 
   local punct_map = output_module.quote_punctuation_map
   if second_char == " " and (first_char == " " or
-      util.endswith(first.value, util.unicode["no-break space"])) then
+        util.endswith(first.value, util.unicode["no-break space"])) then
     second.value = string.sub(second.value, 2)
   elseif punct_map[first_char] then
     if first_char == second_char then
@@ -1703,12 +1656,10 @@
   if first and second then
     local first_char = string.sub(first.value, -1)
     local second_char = string.sub(second.value, 1, 1)
-    -- util.debug(first_char)
-    -- util.debug(second_char)
 
     local punct_map = output_module.quote_punctuation_map
     if second_char == " " and (first_char == " " or
-        util.endswith(first.value, util.unicode["no-break space"])) then
+          util.endswith(first.value, util.unicode["no-break space"])) then
       second.value = string.sub(second.value, 2)
       return true
     elseif punct_map[first_char] then
@@ -1735,7 +1686,7 @@
   local idx = 1
   while idx < #inlines do
     local first = inlines[idx]
-    local second = inlines[idx+1]
+    local second = inlines[idx + 1]
 
     if first._type == "PlainText" and second._type == "PlainText" then
       smash_string_push(first, second)
@@ -1753,7 +1704,7 @@
       end
 
     elseif (first._type == "Formatted" or first._type == "CiteInline"
-        or first._type == "UndefinedCite") and second._type == "PlainText" then
+          or first._type == "UndefinedCite") and second._type == "PlainText" then
       local success = smash_just_punc(first, second)
       if success then
         if second.value == "" then
@@ -1822,7 +1773,7 @@
     -- Move punctuation into quotes as needed
     local first, punctuation_in_quote = find_right_quoted(inlines[idx])
 
-    local second = find_left(inlines[idx+1])
+    local second = find_left(inlines[idx + 1])
     local success = false
     if first and second then
       if punctuation_in_quote then
@@ -1891,7 +1842,6 @@
     return self:write_escaped(inline, context)
 
   elseif type(inline) == "table" then
-    -- util.debug(inline._type)
     if inline._type == "PlainText" then
       return self:write_escaped(inline.value, context)
 
@@ -2016,9 +1966,9 @@
   ["@cite/entry"] = false,
   ["@bibliography/entry"] = function (str, context)
     if string.match(str, "\\bibitem") then
-      str =  str .. "\n"
+      str = str .. "\n"
     else
-      str =  "\\bibitem{".. context.id .. "}\n" .. str .. "\n"
+      str = "\\bibitem{" .. context.id .. "}\n" .. str .. "\n"
     end
     return str
   end,
@@ -2029,7 +1979,7 @@
 }
 
 local latex_escape_table = {
-  ["\\"] = "\\textbackslash{}",
+  ["\\"] = "\\textbackslash ",
   ["{"] = "\\{",
   ["}"] = "\\}",
   ["$"] = "\\$",
@@ -2340,7 +2290,7 @@
 
 local PseudoHtml = HtmlWriter:new()
 
-function PseudoHtml :write_escaped(str, context)
+function PseudoHtml:write_escaped(str, context)
   -- str = string.gsub(str, "%&", "&")
   -- str = string.gsub(str, "<", "<")
   -- str = string.gsub(str, ">", ">")
@@ -2351,7 +2301,7 @@
 end
 
 function PseudoHtml:write_math_ml(inline, context)
-  return string.format('<math>%s</math>', inline.value)
+  return string.format("<math>%s</math>", inline.value)
 end
 
 function PseudoHtml:write_math_tex(inline, context)
@@ -2433,7 +2383,7 @@
     [":"] = ";",
     [","] = ";,",
     ["."] = ";",
-  }
+  },
 }
 
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-unicode.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-unicode.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-unicode.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -7,19 +7,21 @@
 local unicode = {}
 
 local uni_utf8
+local uni_algos_normalize
 local uni_algos_words
 local uni_algos_case
-local util
 
 local using_luatex, kpse = pcall(require, "kpse")
 if using_luatex then
   -- Load `slnunicode` if in LuaTeX
   uni_utf8 = require("unicode").utf8
-  if kpse.find_file("lua-uni-words", "lua") then
-    uni_algos_words = require("lua-uni-words")
+  uni_algos_normalize = require("lua-uni-normalize")
+  local ok
+  ok, uni_algos_words = pcall(require, "lua-uni-words")
+  if not ok then
+    ok, uni_algos_words = pcall(require, "citeproc-lua-uni-words")
   end
   uni_algos_case = require("lua-uni-case")
-  util = require("citeproc-util")
 else
   uni_utf8 = require("lua-utf8")
   if not utf8 then
@@ -28,7 +30,6 @@
   end
   uni_algos_words = require("citeproc.lua-uni-algos.words")
   uni_algos_case = require("citeproc.lua-uni-algos.case")
-  util = require("citeproc.util")
 end
 
 
@@ -54,6 +55,13 @@
 end
 
 
+if using_luatex then
+  unicode.NFC = uni_algos_normalize.NFC
+else
+  unicode.NFC = uni_utf8.normalize_nfc
+end
+
+
 ---Return a casefolded copy of the string. Casefolded strings may be used for caseless matching.
 --- at param str string
 --- at param locale string?
@@ -264,10 +272,8 @@
 --- at param str string
 --- at return string[]
 function unicode.split_word_bounds(str)
-  -- util.debug(str)
   local segments = {}
   if uni_algos_words then
-    -- util.debug("uni_algos_words")
     for _, _, segment in uni_algos_words.word_boundaries(str) do
       table.insert(segments, segment)
     end
@@ -275,12 +281,12 @@
     for i = #segments, 1, -1 do
       local segment = segments[i]
       if segment == "`" then
-        if i < #segments and not uni_utf8.match(segments[i+1], "^%s") then
-          segments[i] = segment .. segments[i+1]
-          table.remove(segments, i+1)
+        if i < #segments and not uni_utf8.match(segments[i + 1], "^%s") then
+          segments[i] = segment .. segments[i + 1]
+          table.remove(segments, i + 1)
         end
-        if i > 1 and not uni_utf8.match(segments[i-1], "%s") then
-          segments[i-1] = segments[i-1] .. segments[i]
+        if i > 1 and not uni_utf8.match(segments[i - 1], "%s") then
+          segments[i - 1] = segments[i - 1] .. segments[i]
           table.remove(segments, i)
         end
       end
@@ -287,7 +293,6 @@
     end
 
   else
-    -- util.debug("no uni_algos_words")
     -- A naive implementation
     local state = CharState.Other
     local segment = ""
@@ -316,7 +321,6 @@
     table.insert(segments, segment)
   end
 
-  -- util.debug(segments)
   return segments
 end
 

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-util.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -109,15 +109,15 @@
 end
 
 
-function util.to_ordinal (n)
+function util.to_ordinal(n)
   -- assert(type(n) == "number")
   local last_digit = n % 10
-  if last_digit == 1 and n ~= 11
-    then return tostring(n) .. "st"
-  elseif last_digit == 2 and n ~= 12
-    then return tostring(n) .. "nd"
-  elseif last_digit == 3 and n ~= 13
-    then return tostring(n) .. "rd"
+  if last_digit == 1 and n ~= 11 then
+    return tostring(n) .. "st"
+  elseif last_digit == 2 and n ~= 12 then
+    return tostring(n) .. "nd"
+  elseif last_digit == 3 and n ~= 13 then
+    return tostring(n) .. "rd"
   else
     return tostring(n) .. "th"
   end
@@ -135,7 +135,7 @@
   if not util.logging_file then
     util.error(string.format("Cannot write to '%s'.", path))
   end
-  util.logging_file = io.open(path, 'a')
+  util.logging_file = io.open(path, "a")
 end
 
 
@@ -239,7 +239,6 @@
 end
 
 -- Similar to re.split() in Python
----comment
 --- at param str string
 --- at param sep string?
 --- at param maxsplit integer?
@@ -256,11 +255,11 @@
     return {}
   end
   if string.find(str, sep) == nil then
-    return { str }
+    return {str}
   end
 
   if maxsplit == nil or maxsplit < 0 then
-    maxsplit = -1    -- No limit
+    maxsplit = -1  -- No limit
   end
   local result = {}
   local pattern = "(.-)" .. sep .. "()"
@@ -320,7 +319,7 @@
   return res
 end
 
-function util.slice (t, start, stop)
+function util.slice(t, start, stop)
   start = start or 1
   stop = stop or #t
   if start < 0 then
@@ -338,13 +337,13 @@
   return new
 end
 
-function util.concat (list, sep)
+function util.concat(list, sep)
   -- This helper function omits empty strings in list, which is different from table.concat
   -- This function always returns a string, even empty.
   local res = ""
   for i = 1, #list do
     local s = list[i]
-    if s and s~= "" then
+    if s and s ~= "" then
       if res == "" then
         res = s
       else
@@ -382,7 +381,7 @@
 
 --- at param str string
 --- at return string
-function util.lstrip (str)
+function util.lstrip(str)
   if not str then
     error("Invalid input")
   end
@@ -405,7 +404,7 @@
 
 --- at param str string
 --- at return string
-function util.rstrip (str)
+function util.rstrip(str)
   if not str then
     error("Invalid input")
   end
@@ -415,7 +414,7 @@
 
 --- at param str string
 --- at return string
-function util.strip (str)
+function util.strip(str)
   return util.lstrip(util.rstrip(str))
 end
 
@@ -423,6 +422,9 @@
   if type(str) ~= "string" then
     util.error(string.format("\n%s\n'%s' is not a string.", debug.traceback(), str))
   end
+  if type(prefix) ~= "string" then
+    util.error(string.format("\n%s\n'%s' is not a string.", debug.traceback(), prefix))
+  end
   return string.sub(str, 1, #prefix) == prefix
 end
 
@@ -430,7 +432,7 @@
   -- if not str or type(str) ~= "string" then
   --   print(debug.traceback())
   -- end
-  return string.sub(str, -#suffix) == suffix
+  return string.sub(str, - #suffix) == suffix
 end
 
 --- at param str string
@@ -629,56 +631,55 @@
 end
 
 util.primary_dialects = {
-  af= "af-ZA",
-  ar= "ar",
-  bg= "bg-BG",
-  ca= "ca-AD",
-  cs= "cs-CZ",
-  cy= "cy-GB",
-  da= "da-DK",
-  de= "de-DE",
-  el= "el-GR",
-  en= "en-US",
-  es= "es-ES",
-  et= "et-EE",
-  eu= "eu",
-  fa= "fa-IR",
-  fi= "fi-FI",
-  fr= "fr-FR",
-  he= "he-IL",
-  hi= "hi-IN",
-  hr= "hr-HR",
-  hu= "hu-HU",
-  id= "id-ID",
-  is= "is-IS",
-  it= "it-IT",
-  ja= "ja-JP",
-  km= "km-KH",
-  ko= "ko-KR",
-  la= "la",
-  lt= "lt-LT",
-  lv= "lv-LV",
-  mn= "mn-MN",
-  nb= "nb-NO",
-  nl= "nl-NL",
-  nn= "nn-NO",
-  pl= "pl-PL",
-  pt= "pt-PT",
-  ro= "ro-RO",
-  ru= "ru-RU",
-  sk= "sk-SK",
-  sl= "sl-SI",
-  sr= "sr-RS",
-  sv= "sv-SE",
-  th= "th-TH",
-  tr= "tr-TR",
-  uk= "uk-UA",
-  vi= "vi-VN",
-  zh= "zh-CN"
+  af = "af-ZA",
+  ar = "ar",
+  bg = "bg-BG",
+  ca = "ca-AD",
+  cs = "cs-CZ",
+  cy = "cy-GB",
+  da = "da-DK",
+  de = "de-DE",
+  el = "el-GR",
+  en = "en-US",
+  es = "es-ES",
+  et = "et-EE",
+  eu = "eu",
+  fa = "fa-IR",
+  fi = "fi-FI",
+  fr = "fr-FR",
+  he = "he-IL",
+  hi = "hi-IN",
+  hr = "hr-HR",
+  hu = "hu-HU",
+  id = "id-ID",
+  is = "is-IS",
+  it = "it-IT",
+  ja = "ja-JP",
+  km = "km-KH",
+  ko = "ko-KR",
+  la = "la",
+  lt = "lt-LT",
+  lv = "lv-LV",
+  mn = "mn-MN",
+  nb = "nb-NO",
+  nl = "nl-NL",
+  nn = "nn-NO",
+  pl = "pl-PL",
+  pt = "pt-PT",
+  ro = "ro-RO",
+  ru = "ru-RU",
+  sk = "sk-SK",
+  sl = "sl-SI",
+  sr = "sr-RS",
+  sv = "sv-SE",
+  th = "th-TH",
+  tr = "tr-TR",
+  uk = "uk-UA",
+  vi = "vi-VN",
+  zh = "zh-CN",
 }
 
 
-
 -- Range delimiter
 
 util.unicode = {
@@ -854,7 +855,7 @@
   util.stop_words[word] = true
 end
 
-function util.all (t)
+function util.all(t)
   for _, item in ipairs(t) do
     if not item then
       return false
@@ -863,7 +864,7 @@
   return true
 end
 
-function util.any (t)
+function util.any(t)
   for _, item in ipairs(t) do
     if item then
       return true
@@ -915,7 +916,7 @@
   {0x2F800, 0x2FA1F},  -- CJK Compatibility Ideographs Supplement
 }
 
-function util.in_list (value, list)
+function util.in_list(value, list)
   for _, v in ipairs(list) do
     if value == v then
       return true
@@ -924,7 +925,7 @@
   return false
 end
 
-function util.in_ranges (value, ranges)
+function util.in_ranges(value, ranges)
   for _, range in ipairs(ranges) do
     if value >= range[1] and value <= range[2] then
       return true
@@ -1000,40 +1001,40 @@
 end
 
 util.ordinal_to_arabic_map = {
-  first              = "1",
-  second             = "2",
-  third              = "3",
-  fourth             = "4",
-  fifth              = "5",
-  sixth              = "6",
-  seventh            = "7",
-  eighth             = "8",
-  ninth              = "9",
-  tenth              = "10",
-  eleventh           = "11",
-  twelfth            = "12",
-  thirteenth         = "13",
-  fourteenth         = "14",
-  fifteenth          = "15",
-  sixteenth          = "16",
-  seventeenth        = "17",
-  eighteenth         = "18",
-  nineteenth         = "19",
-  twentieth          = "20",
-  ["twenty-first"]   = "21",
-  ["twenty-second"]  = "22",
-  ["twenty-third"]   = "23",
-  ["twenty-fourth"]  = "24",
-  ["twenty-fifth"]   = "25",
-  ["twenty-sixth"]   = "26",
+  first = "1",
+  second = "2",
+  third = "3",
+  fourth = "4",
+  fifth = "5",
+  sixth = "6",
+  seventh = "7",
+  eighth = "8",
+  ninth = "9",
+  tenth = "10",
+  eleventh = "11",
+  twelfth = "12",
+  thirteenth = "13",
+  fourteenth = "14",
+  fifteenth = "15",
+  sixteenth = "16",
+  seventeenth = "17",
+  eighteenth = "18",
+  nineteenth = "19",
+  twentieth = "20",
+  ["twenty-first"] = "21",
+  ["twenty-second"] = "22",
+  ["twenty-third"] = "23",
+  ["twenty-fourth"] = "24",
+  ["twenty-fifth"] = "25",
+  ["twenty-sixth"] = "26",
   ["twenty-seventh"] = "27",
-  ["twenty-eighth"]  = "28",
-  ["twenty-ninth"]   = "29",
-  thirtieth          = "30",
+  ["twenty-eighth"] = "28",
+  ["twenty-ninth"] = "29",
+  thirtieth = "30",
 }
 
 
-function util.convert_roman (number)
+function util.convert_roman(number)
   -- assert(type(number) == "number")
   local output = {}
   for _, tuple in ipairs(util.roman_numerals) do
@@ -1045,19 +1046,19 @@
 end
 
 util.roman_numerals = {
-  {"m",  1000},
+  {"m", 1000},
   {"cm", 900},
-  {"d",  500},
+  {"d", 500},
   {"cd", 400},
-  {"c",  100},
+  {"c", 100},
   {"xc", 90},
-  {"l",  50},
+  {"l", 50},
   {"xl", 40},
-  {"x",  10},
+  {"x", 10},
   {"ix", 9},
-  {"v",  5},
+  {"v", 5},
   {"iv", 4},
-  {"i",  1},
+  {"i", 1},
 };
 
 
@@ -1234,8 +1235,15 @@
   file:close()
 end
 
+--- at alias CslDateVariable { date-parts: (string|number)[][]?, season: string|number, circa: boolean, literal: string|number?, raw: string?}
 
+
+--- at param str string
+--- at return CslDateVariable?
 function util.parse_edtf(str)
+  if string.match(str, "^%s*$") then
+    return nil
+  end
   local date = {["date-parts"] = {}}
   local range_parts = util.split(str, "/")
   for i, range_part in ipairs(range_parts) do
@@ -1244,10 +1252,10 @@
     end
     date["date-parts"][i] = {}
     local negative_year = false
-    if string.match(range_part, "^Y%-") then
+    if string.match(range_part, "^%-") or string.match(range_part, "^Y%-") then
       negative_year = true
     end
-    range_part = string.gsub(range_part, "^Y[+-]?", "")
+    range_part = string.gsub(range_part, "^Y?[+-]?", "")
     if string.match(range_part, "[?~%%]$") then
       date.circa = true
       range_part = string.gsub(range_part, "[?~%%]$", "")
@@ -1262,20 +1270,34 @@
         date_part = string.gsub(date_part, "X", "0")
       end
       if string.match(date_part, "^%d+$") then
-        date_part = tonumber(date_part)
-        if date_part > 0 then
-          date["date-parts"][i][j] = date_part
+        local date_part_number = tonumber(date_part)
+        if date_part_number > 0 then
+          date["date-parts"][i][j] = date_part_number
         else
           break
         end
-      else
-        return nil
+      elseif date_part ~= "" then
+        -- util.error(string.format('Invalid EDTF date "%s".', str))
+        return {literal = str}
       end
     end
     if negative_year then
-      date["date-parts"][i][1] = - date["date-parts"][i][1]
+      date["date-parts"][i][1] = -1 - date["date-parts"][i][1]
     end
   end
+
+  local all_empty = true
+  for i, range_part in ipairs(date["date-parts"]) do
+    if #range_part > 0 then
+      all_empty = false
+      break
+    end
+  end
+
+  if all_empty then
+    return nil
+  end
+
   return date
 end
 
@@ -1328,7 +1350,6 @@
 util.trigraph = "Aaaa00:AaAa00:AaAA00:AAAA00"
 
 
----comment
 --- at param trigraph string
 --- at return { authors: integer[], year: integer }
 function util.get_trigraph_param(trigraph)
@@ -1346,7 +1367,7 @@
       table.insert(param.authors, 1)
     elseif char == "a" then
       local len = #param.authors
-      param.authors[len] =  param.authors[len] + 1
+      param.authors[len] = param.authors[len] + 1
     elseif char == "0" then
       param.year = param.year + 1
     else
@@ -1357,7 +1378,6 @@
 end
 
 
----comment
 --- at param item ItemData
 --- at return string
 function util.get_citation_label(item)

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-yaml.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-yaml.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc-yaml.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -36,7 +36,7 @@
   end
 
   -- The items exported from Better BibTeX has a {references = []} structure.
-  if type(items) == 'table' and items.references then
+  if type(items) == "table" and items.references then
     items = items.references
   end
 
@@ -57,7 +57,7 @@
 
         elseif type(value) == "table" and not value["date-parts"] then
           local new_date = {
-            ["date-parts"] = {}
+            ["date-parts"] = {},
           }
 
           if value.year then

Modified: trunk/Master/texmf-dist/scripts/citation-style-language/citeproc.lua
===================================================================
--- trunk/Master/texmf-dist/scripts/citation-style-language/citeproc.lua	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/scripts/citation-style-language/citeproc.lua	2025-04-29 19:47:32 UTC (rev 75059)
@@ -17,7 +17,7 @@
   util = require("citeproc.util")
 end
 
-citeproc.__VERSION__ = "0.7.0"
+citeproc.__VERSION__ = "0.8.0"
 
 citeproc.new = engine.CiteProc.new
 citeproc.util = util

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-cite.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-cite.sty	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-cite.sty	2025-04-29 19:47:32 UTC (rev 75059)
@@ -21,6 +21,8 @@
       }
   }
 
+\NewCommandCopy \autocite \cite
+
 \NewDocumentCommand \parencite { s o o m }
   {
     \IfBooleanTF {#1}
@@ -49,12 +51,16 @@
 \NewDocumentCommand \cites { }
   { \__csl_cites: }
 
+\NewDocumentCommand \parencites { }
+  { \__csl_cites: }
+
 \NewDocumentCommand \citeauthor { o o m }
   { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { author-only } }
 
-\NewDocumentCommand \citeyear{ o o m }
+\NewDocumentCommand \citeyear { o o m }
   { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { cite-year } }
 
+
 % Suppresses the author (from `natbib`).
 \NewDocumentCommand \citeyearpar { o o m }
   { \__csl_cite_in_text:nnnn {#1} {#2} {#3} { suppress-author } }
@@ -66,6 +72,7 @@
 \seq_new:N \l__csl_cite_keys_seq
 \seq_new:N \l__csl_citation_items_seq
 \prop_new:N \l__csl_citation_properties_prop
+\bool_new:N \l__csl_citation_unsorted_bool
 \prop_new:N \l__csl_citation_info_prop
 
 % #1: prenote
@@ -193,6 +200,7 @@
     \seq_clear:N \l__csl_cite_keys_seq
     \seq_clear:N \l__csl_citation_items_seq
     \prop_clear:N \l__csl_citation_properties_prop
+    \bool_set_false:N \l__csl_citation_unsorted_bool
     \tl_clear:N \l__csl_cite_prefix_tl
     \tl_clear:N \l__csl_cite_suffix_tl
   }
@@ -342,6 +350,7 @@
     volume          .code:n = { \__csl_set_locator:nn { volume          } {#1} } ,
     % Citation properties
     infix           .prop_put:N = \l__csl_citation_properties_prop,
+    unsorted        .bool_set:N = \l__csl_citation_unsorted_bool
   }
 
 
@@ -359,6 +368,10 @@
     \prop_put:NnV \l__csl_citation_properties_prop { noteIndex } \l__csl_note_index_tl
     \__csl_make_chapter_property:
     \__csl_add_back_ref_info:
+    \bool_if:NT \l__csl_citation_unsorted_bool
+      {
+        \prop_put:Nnn \l__csl_citation_properties_prop { unsorted } { true }
+      }
   }
 
 \cs_new:Npn \__csl_make_chapter_property:

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-compatible.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-compatible.sty	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-compatible.sty	2025-04-29 19:47:32 UTC (rev 75059)
@@ -8,7 +8,6 @@
 
 % ### `babel`
 
-% This should be disabled.
 \hook_gput_code:nnn { package / babel / after } { . }
   {
     \RenewDocumentCommand \nocite { m }
@@ -21,6 +20,31 @@
     \cs_set_eq:NN \bbl at cite@choice \relax
     \cs_set_eq:NN \@lbibitem \__csl_lbibitem_plain:nn
     \cs_set_eq:NN \@bibitem \__csl_bibitem_plain:n
+    %
+    \cs_set:Npn \__csl_get_locale_from_babel:
+      {
+        % `babel`'s main language
+        \tl_if_exist:NT \bbl at main@language
+          {
+            \prop_get:NVNTF \l__csl_language_code_map_prop \bbl at main@language
+              \l__csl_locale_tl
+              {
+                \msg_info:nnVV { citation-style-language }
+                  { locale-from-babel-language } \l__csl_locale_tl
+                  \bbl at main@language
+              }
+              {
+                \msg_warning:nnV { citation-style-language }
+                  { unrecognized-babel-language } \bbl at main@language
+                \tl_clear:N \l__csl_locale_tl
+              }
+          }
+      }
+    %
+    \msg_new:nnn { citation-style-language } { locale-from-babel-language }
+      { CSL~ locale~ "#1"~ from~ babel~ language~ "#2". }
+    \msg_new:nnn { citation-style-language } { unrecognized-babel-language }
+      { Unrecognized~ babel~ language~ "#1". }
   }
 
 
@@ -97,29 +121,6 @@
 
 % ### `hyperref`
 
-% The hyperref package also patches \bibcite but it cannot provide hyperlinks
-% when used with csl.
-\bool_new:N \l__csl_hyperref_loaded_bool
-\hook_gput_code:nnn { package / hyperref / after } { . }
-  {
-    \bool_set_true:N \l__csl_hyperref_loaded_bool
-    % Pakcage "hyperref" redefines \@lbibitem and \bibitem and we need to
-    % recover them.
-    % In non-implicit mode (e.g., loaded by `beamer`), hyperref stops early
-    % (`\MaybeStopEarly`) and it doesn't redefine the cite internal commands.
-    \cs_if_exist:NT \@extra at b@citeb
-      {
-        \cs_gset_eq:NN \cslcite \__csl_hyperref_cite_item:nn
-        \cs_gset_eq:NN \@lbibitem \__csl_lbibitem:
-        \cs_gset_eq:NN \@bibitem \__csl_bibitem:
-        \cs_gset_eq:NN \__csl_lbibitem_plain:nn \__csl_hyperref_lbibitem:nn
-        \cs_gset_eq:NN \__csl_bibitem_plain:n \__csl_hyperref_bibitem:n
-        \cs_gset_eq:NN \__csl_process_entry_ids:n \__csl_hyperref_process_entry_ids:n
-        \cs_gset_eq:NN \__csl_process_excluded_ids:n \__csl_hyperref_process_excluded_ids:n
-        \cs_gset_eq:NN \__csl_read_entry_ids: \__csl_hyperref_read_entry_ids:
-      }
-  }
-
 \clist_new:N \l__csl_ref_section_entry_ids_clist
 \clist_new:N \l__csl_ref_section_excluded_ids_clist
 
@@ -251,7 +252,33 @@
       { \clist_set:NV \l__csl_ref_section_excluded_ids_clist \l_tmpa_tl }
   }
 
+\bool_new:N \l__csl_hyperref_loaded_bool
 
+% If `hyperref` is loaded before `csl`, the following code is executed
+% immediately via a one-time hook.
+% Thus we should put it after all of the previous definitions
+% (<https://github.com/zepinglee/citeproc-lua/issues/91>).
+\hook_gput_code:nnn { package / hyperref / after } { . }
+  {
+    \bool_set_true:N \l__csl_hyperref_loaded_bool
+    % Package `hyperref` redefines \@lbibitem and \bibitem and we need to
+    % recover them.
+    % In non-implicit mode (e.g., loaded by `beamer`), hyperref stops early
+    % (`\MaybeStopEarly`) and it doesn't redefine the cite internal commands.
+    \cs_if_exist:NT \@extra at b@citeb
+      {
+        \cs_gset_eq:NN \cslcite \__csl_hyperref_cite_item:nn
+        \cs_gset_eq:NN \@lbibitem \__csl_lbibitem:
+        \cs_gset_eq:NN \@bibitem \__csl_bibitem:
+        \cs_gset_eq:NN \__csl_lbibitem_plain:nn \__csl_hyperref_lbibitem:nn
+        \cs_gset_eq:NN \__csl_bibitem_plain:n \__csl_hyperref_bibitem:n
+        \cs_gset_eq:NN \__csl_process_entry_ids:n \__csl_hyperref_process_entry_ids:n
+        \cs_gset_eq:NN \__csl_process_excluded_ids:n \__csl_hyperref_process_excluded_ids:n
+        \cs_gset_eq:NN \__csl_read_entry_ids: \__csl_hyperref_read_entry_ids:
+      }
+  }
+
+
 % ### `perpage`
 
 \hook_gput_code:nnn { package / perpage / after } { . }

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language-init.sty	2025-04-29 19:47:32 UTC (rev 75059)
@@ -279,13 +279,7 @@
     \clist_clear:N \l_tmpa_clist  % list of options to write to aux file
     % locale
     \tl_if_empty:NT \l__csl_locale_tl
-      {
-        \tl_if_exist:NT \bbl at main@language
-          {
-            \prop_get:NVN \l__csl_language_code_map_prop \bbl at main@language
-              \l__csl_locale_tl
-          }
-      }
+      { \__csl_get_locale_from_babel: }
     \tl_if_empty:NF \l__csl_locale_tl
       {
         \clist_put_right:Ne \l_tmpa_clist
@@ -303,6 +297,9 @@
       }
   }
 
+% This is the hook for `babel` package.
+\cs_new:Npn \__csl_get_locale_from_babel: { }
+
 \cs_new:Npn \__csl_write_aux_options:n #1
   {
     \if at filesw

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/citation-style-language.sty	2025-04-29 19:47:32 UTC (rev 75059)
@@ -9,10 +9,9 @@
 \RequirePackage{expl3}
 \RequirePackage{xparse}
 
-\ProvidesExplPackage {citation-style-language} {2025-02-23} {0.7.0}
+\ProvidesExplPackage {citation-style-language} {2025-04-29} {0.8.0}
   {Citation Style Language for LaTeX}
 
-\RequirePackage { l3keys2e }
 \RequirePackage { url }
 
 
@@ -148,7 +147,7 @@
   }
 
 
-\ProcessKeysPackageOptions { csl }
+\ProcessKeyOptions [ csl ]
 
 
 \DeclareDocumentCommand \bibliographystyle { m }

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-ro-RO.xml
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-ro-RO.xml	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-ro-RO.xml	2025-04-29 19:47:32 UTC (rev 75059)
@@ -32,7 +32,7 @@
     <term name="loc-cit">loc. cit.</term> <!-- like ibid., the abbreviated form is the regular form  -->
     <term name="no-place">fără loc</term>
     <term name="no-place" form="short">f. l.</term>
-    <term name="no-publisher">fără editorr</term> <!-- sine nomine -->
+    <term name="no-publisher">fără editor</term> <!-- sine nomine -->
     <term name="no-publisher" form="short">f. e.</term>
     <term name="on">pe</term>
     <term name="op-cit">op. cit.</term> <!-- like ibid., the abbreviated form is the regular form  -->
@@ -45,7 +45,7 @@
     <term name="radio-series">serie radio</term>
     <term name="radio-series-episode">episod de serie radio</term>
     <term name="special-issue">număr special</term>
-    <term name="special-section">secțiune special</term>
+    <term name="special-section">secțiune specială</term>
     <term name="television-broadcast">emisiune de televiziune</term>
     <term name="television-series">serial televizat</term>
     <term name="television-series-episode">episod al serialului televizat</term>
@@ -77,7 +77,7 @@
     <term name="first-reference-note-number" form="short">cf.</term>
     <term name="number" form="short">nr.</term>
     <term name="edition" form="short">ed.</term>
-    <term name="et-al">și colab.</term>
+    <term name="et-al">et al.</term> <!-- Romanian translation of this Latin expression is never used in Romanian styles -->
     <term name="forthcoming">în curs de apariție</term>
     <term name="from">din</term>
     <term name="ibid">ibidem</term>
@@ -184,7 +184,7 @@
     <term name="close-quote">”</term>
     <term name="open-inner-quote">«</term>
     <term name="close-inner-quote">»</term>
-    <term name="page-range-delimiter">–</term>
+    <term name="page-range-delimiter">-</term>
     <term name="colon">:</term>
     <term name="comma">,</term>
     <term name="semicolon">;</term>
@@ -394,7 +394,7 @@
     </term>
     <term name="number-of-volumes" form="short">
       <single>vol.</single>
-      <multiple>vols.</multiple>
+      <multiple>vol.</multiple>
     </term>
     <term name="page-first" form="short">
       <single>p.</single>
@@ -402,7 +402,7 @@
     </term>
     <term name="printing" form="short">
       <single>print.</single>
-      <multiple>prints.</multiple>
+      <multiple>print.</multiple>
     </term>
     
 
@@ -414,8 +414,8 @@
     <term name="part" form="short">part.</term>
     <term name="section" form="short">sec.</term>
     <term name="supplement" form="short">
-      <single>supp.</single>
-      <multiple>supps.</multiple>
+      <single>suplim.</single>
+      <multiple>suplim.</multiple>
     </term>
     <term name="sub-verbo" form="short">
       <single>s.v.</single>
@@ -454,8 +454,8 @@
 
     <!-- LONG ROLE FORMS -->
     <term name="collection-editor">
-      <single>ed.</single>
-      <multiple>eds.</multiple>
+      <single>editorul seriei</single>
+      <multiple>editorii seriei</multiple>
     </term>
     <term name="chair">
       <single>președinte</single>
@@ -467,11 +467,11 @@
     </term>
     <term name="contributor">
       <single>contributor</single>
-      <multiple>contributorii</multiple>
+      <multiple>contributori</multiple>
     </term>
     <term name="curator">
       <single>curator</single>
-      <multiple>curators</multiple>
+      <multiple>curatori</multiple>
     </term>
     <term name="executive-producer">
       <single>producător executiv</single>
@@ -483,7 +483,7 @@
     </term>
     <term name="host">
       <single>gazdă</single>
-      <multiple>gazdele</multiple>
+      <multiple>gazde</multiple>
     </term>
     <term name="narrator">
       <single>narator</single>
@@ -544,8 +544,8 @@
       <multiple>contrib.</multiple>
     </term>
     <term name="curator" form="short">
-      <single>cur.</single>
-      <multiple>cur.</multiple>
+      <single>curat.</single>
+      <multiple>curat.</multiple>
     </term>
     <term name="executive-producer" form="short">
       <single>prod. exec.</single>
@@ -557,23 +557,23 @@
     </term>
     <term name="organizer" form="short">
       <single>org.</single>
-      <multiple>orgs.</multiple>
+      <multiple>org.</multiple>
     </term>
     <term name="performer" form="short">
-      <single>perf.</single>
-      <multiple>perfs.</multiple>
+      <single>interpr.</single>
+      <multiple>interpr..</multiple>
     </term>
     <term name="producer" form="short">
       <single>prod.</single>
-      <multiple>prods.</multiple>
+      <multiple>prod.</multiple>
     </term>
     <term name="script-writer" form="short">
-      <single>writ.</single>
-      <multiple>writs.</multiple>
+      <single>scriit.</single>
+      <multiple>scriit.</multiple>
     </term>
     <term name="series-creator" form="short">
-      <single>cre.</single>
-      <multiple>cres.</multiple>
+      <single>ed. col.</single>
+      <multiple>ed. col.</multiple>
     </term>
     <term name="director" form="short">
       <single>dir.</single>
@@ -602,23 +602,23 @@
 
     <!-- VERB ROLE FORMS -->
     <term name="collection-editor" form="verb">editat de</term>
-    <term name="chair" form="verb">chaired by</term>
-    <term name="compiler" form="verb">compiled by</term>
-    <term name="contributor" form="verb">alături de</term>
-    <term name="curator" form="verb">verificat de</term>
+    <term name="chair" form="verb">condus de</term>
+    <term name="compiler" form="verb">compilat de</term>
+    <term name="contributor" form="verb">cu contribuția</term>
+    <term name="curator" form="verb">curator</term>
     <term name="executive-producer" form="verb">produs executiv de</term>
     <term name="guest" form="verb">cu invitatul</term>
     <term name="host" form="verb">găzduit de</term>
     <term name="narrator" form="verb">narat de</term>
     <term name="organizer" form="verb">organizat de</term>
-    <term name="performer" form="verb">performed by</term>
+    <term name="performer" form="verb">realizat de</term>
     <term name="producer" form="verb">produs de</term>
     <term name="script-writer" form="verb">scris de</term>
-    <term name="series-creator" form="verb">creat de</term>
+    <term name="series-creator" form="verb">colecție editată de</term>
     <term name="container-author" form="verb">de</term>
     <term name="director" form="verb">coordonat de</term>
     <term name="editor" form="verb">ediție de</term>
-    <term name="editorial-director" form="verb">coordonator</term>
+    <term name="editorial-director" form="verb">coordonat de</term>
     <term name="illustrator" form="verb">ilustrații de</term>
     <term name="interviewer" form="verb">interviu de</term>
     <term name="recipient" form="verb">în</term>
@@ -630,21 +630,21 @@
     <!-- SHORT VERB ROLE FORMS -->
     <term name="compiler" form="verb-short">comp. de</term>
     <term name="contributor" form="verb-short">cu</term>
-    <term name="curator" form="verb-short">cur. by</term>
+    <term name="curator" form="verb-short">cur.</term>
     <term name="executive-producer" form="verb-short">prod. exec. de</term>
-    <term name="guest" form="verb-short">w. guest</term>
-    <term name="host" form="verb-short">hosted by</term>
+    <term name="guest" form="verb-short">cu invit.</term>
+    <term name="host" form="verb-short">găzd. de</term>
     <term name="narrator" form="verb-short">nar. de</term>
-    <term name="organizer" form="verb-short">org. by</term>
-    <term name="performer" form="verb-short">perf. by</term>
+    <term name="organizer" form="verb-short">org. de</term>
+    <term name="performer" form="verb-short">interpr. de</term>
     <term name="producer" form="verb-short">prod. de</term>
-    <term name="script-writer" form="verb-short">scr. de</term>
-    <term name="series-creator" form="verb-short">cre. de</term>
+    <term name="script-writer" form="verb-short">scen. de</term>
+    <term name="series-creator" form="verb-short">ed. col. de</term>
     <term name="director" form="verb-short">dir.</term>
-    <term name="editor" form="verb-short">ed.</term>
-    <term name="editorial-director" form="verb-short">coord.</term>
-    <term name="illustrator" form="verb-short">ilustr.</term>
-    <term name="translator" form="verb-short">trad.</term>
+    <term name="editor" form="verb-short">ed. de</term>
+    <term name="editorial-director" form="verb-short">coord. de</term>
+    <term name="illustrator" form="verb-short">ilustr. de</term>
+    <term name="translator" form="verb-short">trad. de</term>
     <term name="editortranslator" form="verb-short">ed. și trad. de</term>
 
     <!-- LONG MONTH FORMS -->

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-th-TH.xml
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-th-TH.xml	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/locales/csl-locales-th-TH.xml	2025-04-29 19:47:32 UTC (rev 75059)
@@ -4,6 +4,9 @@
     <translator>
       <name>Dusit Laohasinnarong</name>
     </translator>
+    <translator>
+      <name>Watcharakorn Kaobath</name>
+    </translator>
     <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
     <updated>2012-07-04T23:31:02+00:00</updated>
   </info>
@@ -52,7 +55,7 @@
     <term name="anonymous">นิรนาม</term>
     <term name="anonymous" form="short">นิรนาม</term>
     <term name="at">ที่</term>
-    <term name="available at">available at</term>
+    <term name="available at">เข้าถึงได้จาก</term>
     <term name="by">โดย</term>
     <term name="circa">โดยประมาณ</term>
     <term name="circa" form="short">ประมาณ</term>

Modified: trunk/Master/texmf-dist/tex/latex/citation-style-language/styles/modern-humanities-research-association.csl
===================================================================
--- trunk/Master/texmf-dist/tex/latex/citation-style-language/styles/modern-humanities-research-association.csl	2025-04-29 19:46:50 UTC (rev 75058)
+++ trunk/Master/texmf-dist/tex/latex/citation-style-language/styles/modern-humanities-research-association.csl	2025-04-29 19:47:32 UTC (rev 75059)
@@ -1,37 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
-<style xmlns="http://purl.org/net/xbiblio/csl" class="note" version="1.0" demote-non-dropping-particle="sort-only" page-range-format="chicago" default-locale="en-GB">
+<style xmlns="http://purl.org/net/xbiblio/csl" and="text" class="note" demote-non-dropping-particle="sort-only" et-al-min="4" et-al-use-first="1" page-range-format="minimal-two" version="1.0" default-locale="en-GB">
   <info>
-    <title>Modern Humanities Research Association 4th edition (note with bibliography)</title>
+    <title>Modern Humanities Research Association, 4th edition (note with bibliography)</title>
     <title-short>MHRA</title-short>
     <id>http://www.zotero.org/styles/modern-humanities-research-association</id>
     <link href="http://www.zotero.org/styles/modern-humanities-research-association" rel="self"/>
-    <link href="http://www.mhra.org.uk/Publications/Books/StyleGuide/download.shtml" rel="documentation"/>
+    <link href="https://www.mhra.org.uk/style/" rel="documentation"/>
     <author>
       <name>Rintze Zelle</name>
-      <uri>http://twitter.com/rintzezelle</uri>
+      <uri>https://orcid.org/0000-0003-1779-8883</uri>
     </author>
+    <author>
+      <name>Andrew Dunning</name>
+      <uri>https://orcid.org/0000-0003-0464-5036</uri>
+    </author>
     <contributor>
       <name>Sebastian Karcher</name>
+      <uri>https://orcid.org/0000-0001-8249-7388</uri>
     </contributor>
     <contributor>
-      <name>Andrew Dunning</name>
-      <uri>https://orcid.org/0000-0003-0464-5036</uri>
-    </contributor>
-    <contributor>
       <name>Patrick O'Brien</name>
     </contributor>
     <category citation-format="note"/>
     <category field="generic-base"/>
-    <summary>MHRA format with full notes and bibliography</summary>
-    <updated>2024-10-30T12:10:40-04:00</updated>
+    <category field="humanities"/>
+    <summary>MHRA Style Guide full notes and bibliography</summary>
+    <updated>2025-03-19T08:12:42-04:00</updated>
     <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
   </info>
   <locale xml:lang="en">
     <terms>
       <term name="et-al">and others</term>
-      <term name="editor" form="verb-short">ed. by</term>
-      <term name="edition" form="short">edn</term>
-      <term name="translator" form="verb-short">trans. by</term>
+      <term form="short" name="edition">edn</term>
       <term name="folio">
         <single>fol.</single>
         <multiple>fols</multiple>
@@ -38,27 +38,35 @@
       </term>
     </terms>
   </locale>
-  <macro name="author">
-    <group delimiter=". ">
-      <names variable="author">
-        <name name-as-sort-order="first" and="text" sort-separator=", " delimiter=", " delimiter-precedes-last="always"/>
-        <label form="short" prefix=", " suffix="."/>
-        <substitute>
-          <names variable="editor"/>
-          <names variable="translator"/>
-          <text macro="title-note"/>
-        </substitute>
-      </names>
-      <text macro="recipient"/>
-    </group>
+  <macro name="title-sort-substitute">
+    <choose>
+      <if match="any" type="bill book graphic legal_case legislation motion_picture report song">
+        <text font-style="italic" form="short" text-case="title" variable="title"/>
+      </if>
+      <else>
+        <text form="short" quotes="true" text-case="title" variable="title"/>
+      </else>
+    </choose>
   </macro>
+  <macro name="recipient-short">
+    <names variable="recipient">
+      <label form="verb" suffix=" "/>
+      <name form="short"/>
+    </names>
+  </macro>
   <macro name="recipient">
+    <names variable="recipient">
+      <label form="verb" suffix=" "/>
+      <name/>
+    </names>
+  </macro>
+  <macro name="communication-recipient">
     <group delimiter=" ">
       <choose>
         <if type="personal_communication">
           <choose>
             <if variable="genre">
-              <text variable="genre" text-case="capitalize-first"/>
+              <text text-case="capitalize-first" variable="genre"/>
             </if>
             <else>
               <text term="letter" text-case="capitalize-first"/>
@@ -66,245 +74,297 @@
           </choose>
         </if>
       </choose>
-      <text macro="recipient-note"/>
+      <text macro="recipient"/>
     </group>
   </macro>
+  <macro name="contributors-short">
+    <group delimiter=" ">
+      <names variable="author">
+        <name form="short"/>
+        <substitute>
+          <names variable="editor"/>
+          <names variable="translator"/>
+          <text macro="title-sort-substitute"/>
+        </substitute>
+      </names>
+      <text macro="recipient-short"/>
+    </group>
+  </macro>
   <macro name="contributors-note">
-    <names variable="author">
-      <name and="text" sort-separator=", " delimiter=", "/>
-      <label form="short" prefix=", "/>
-      <substitute>
-        <text macro="title-note"/>
-      </substitute>
-    </names>
-    <text macro="recipient-note"/>
+    <group delimiter=" ">
+      <names variable="author">
+        <label form="short" prefix=", "/>
+        <substitute>
+          <text macro="title"/>
+        </substitute>
+      </names>
+      <text macro="recipient"/>
+    </group>
   </macro>
-  <macro name="title-note">
-    <choose>
-      <if variable="title" match="none">
-        <text variable="genre"/>
-      </if>
-      <else-if type="bill book graphic legislation motion_picture report song" match="any">
-        <text variable="title" text-case="title" font-style="italic"/>
-        <group delimiter=" " prefix=", ">
-          <text term="version"/>
-          <text variable="version"/>
-        </group>
-      </else-if>
-      <else-if type="legal_case interview" match="any">
-        <text variable="title"/>
-      </else-if>
-      <else-if variable="reviewed-author">
-        <text variable="title" font-style="italic" prefix="review of "/>
-      </else-if>
-      <else>
-        <text variable="title" text-case="title" quotes="true"/>
-      </else>
-    </choose>
+  <macro name="contributors">
+    <group delimiter=". ">
+      <names variable="author">
+        <name delimiter-precedes-last="always" name-as-sort-order="first"/>
+        <label form="short" prefix=", "/>
+        <substitute>
+          <names variable="editor"/>
+          <names variable="translator"/>
+          <text macro="title"/>
+        </substitute>
+      </names>
+      <text macro="communication-recipient"/>
+    </group>
   </macro>
   <macro name="title-subsequent">
     <choose>
-      <if variable="title" match="none">
+      <if match="none" variable="title">
         <text macro="issued"/>
       </if>
-      <else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
-        <text variable="title" font-style="italic" text-case="title" form="short"/>
+      <else-if match="any" type="bill book graphic legal_case legislation motion_picture report song">
+        <text font-style="italic" form="short" text-case="title" variable="title"/>
       </else-if>
       <else>
-        <text variable="title" quotes="true" text-case="title" form="short"/>
+        <text form="short" quotes="true" text-case="title" variable="title"/>
       </else>
     </choose>
   </macro>
-  <macro name="title-sort-substitute">
+  <macro name="title">
     <choose>
-      <if type="bill book graphic legal_case legislation motion_picture report song" match="any">
-        <text variable="title" font-style="italic" text-case="title" form="short"/>
+      <if match="none" variable="title">
+        <text variable="genre"/>
       </if>
+      <else-if match="any" type="bill book graphic legislation motion_picture report song">
+        <group delimiter=", ">
+          <text font-style="italic" text-case="title" variable="title"/>
+          <group delimiter=" ">
+            <text term="version"/>
+            <text variable="version"/>
+          </group>
+        </group>
+      </else-if>
+      <else-if match="any" type="legal_case interview">
+        <text variable="title"/>
+      </else-if>
+      <else-if variable="reviewed-author">
+        <text font-style="italic" prefix="review of " variable="title"/>
+      </else-if>
       <else>
-        <text variable="title" quotes="true" text-case="title" form="short"/>
+        <text quotes="true" text-case="title" variable="title"/>
       </else>
     </choose>
   </macro>
   <macro name="editor-translator">
     <group delimiter=", ">
-      <group delimiter=" ">
-        <choose>
-          <if variable="container-author reviewed-author" match="any">
-            <group>
-              <names variable="container-author reviewed-author">
-                <label form="verb-short" text-case="lowercase" suffix=" "/>
-                <name and="text" delimiter=", "/>
-              </names>
-            </group>
-          </if>
-        </choose>
-      </group>
-      <names variable="editor translator director" delimiter=", ">
-        <label form="verb-short" text-case="lowercase" suffix=" "/>
-        <name and="text" delimiter=", "/>
+      <choose>
+        <if match="any" variable="container-author reviewed-author">
+          <names delimiter=", " variable="container-author reviewed-author">
+            <label form="verb-short" suffix=" "/>
+            <name/>
+          </names>
+        </if>
+      </choose>
+      <names delimiter=", " variable="editor translator director">
+        <label form="verb-short" suffix=" "/>
+        <name/>
       </names>
     </group>
   </macro>
-  <macro name="secondary-contributors-note">
+  <macro name="secondary-contributors">
     <choose>
-      <if type="chapter paper-conference" match="none">
+      <if match="none" type="chapter entry-dictionary entry-encyclopedia paper-conference">
         <text macro="editor-translator"/>
       </if>
     </choose>
   </macro>
-  <macro name="container-contributors-note">
-    <choose>
-      <if type="chapter paper-conference" match="any">
-        <text macro="editor-translator"/>
-      </if>
-    </choose>
-  </macro>
-  <macro name="collection-title">
-    <group delimiter=", ">
+  <macro name="container-title">
+    <group delimiter=" ">
       <choose>
-        <if type="article-journal">
-          <text variable="collection-title"/>
-          <text variable="collection-number"/>
+        <if match="any" type="chapter entry-dictionary entry-encyclopedia paper-conference">
+          <text term="in"/>
         </if>
-        <else>
-          <text variable="collection-title" text-case="title"/>
-          <text variable="collection-number"/>
-        </else>
       </choose>
+      <text font-style="italic" text-case="title" variable="container-title"/>
     </group>
   </macro>
-  <macro name="locators-note">
+  <macro name="container-contributors">
     <choose>
-      <if type="article-journal">
-        <group delimiter=".">
-          <text variable="volume"/>
-          <text variable="issue"/>
-        </group>
+      <if match="any" type="chapter entry-dictionary entry-encyclopedia paper-conference">
+        <text macro="editor-translator"/>
       </if>
-      <else-if type="bill book chapter graphic legal_case legislation motion_picture paper-conference report song" match="any">
-        <group delimiter=", ">
-          <text macro="edition-note"/>
-          <group>
-            <number variable="number-of-volumes" form="numeric"/>
-            <text term="volume" form="short" prefix=" " plural="true"/>
-          </group>
-        </group>
-      </else-if>
     </choose>
   </macro>
-  <macro name="volume">
+  <macro name="edition">
     <choose>
+      <if match="any" type="bill book chapter entry-dictionary entry-encyclopedia graphic legal_case legislation motion_picture paper-conference report song">
+        <choose>
+          <if is-numeric="edition">
+            <group delimiter=" ">
+              <number form="ordinal" variable="edition"/>
+              <text form="short" term="edition"/>
+            </group>
+          </if>
+          <else>
+            <text variable="edition"/>
+          </else>
+        </choose>
+      </if>
+    </choose>
+  </macro>
+  <macro name="locators">
+    <choose>
       <if type="article-journal">
-        <group delimiter=".">
-          <text variable="volume"/>
-          <text variable="issue"/>
+        <group delimiter=", ">
+          <text variable="collection-title"/>
+          <choose>
+            <if variable="volume">
+              <group delimiter=".">
+                <text variable="volume"/>
+                <text variable="issue"/>
+              </group>
+            </if>
+            <else>
+              <group delimiter=" ">
+                <text form="short" term="issue"/>
+                <text variable="issue"/>
+              </group>
+            </else>
+          </choose>
         </group>
       </if>
-      <else-if type="bill book chapter graphic legal_case legislation motion_picture paper-conference report song" match="any">
+      <else-if match="any" type="bill book chapter entry-dictionary entry-encyclopedia graphic legal_case legislation motion_picture paper-conference report song">
         <group delimiter=", ">
-          <text macro="edition-note"/>
-          <group>
-            <number variable="number-of-volumes" form="numeric"/>
-            <text term="volume" form="short" prefix=" " plural="true"/>
+          <group delimiter=", ">
+            <text text-case="title" variable="collection-title"/>
+            <text variable="collection-number"/>
           </group>
+          <text macro="edition"/>
+          <group delimiter=" ">
+            <number form="numeric" variable="number-of-volumes"/>
+            <text form="short" plural="true" term="volume"/>
+          </group>
         </group>
       </else-if>
     </choose>
   </macro>
-  <macro name="issue-note">
+  <macro name="event">
+    <group delimiter=" ">
+      <text term="presented at"/>
+      <text variable="event"/>
+    </group>
+  </macro>
+  <macro name="publisher">
     <choose>
-      <if type="article-journal">
+      <if type="thesis">
+        <text variable="publisher"/>
+      </if>
+      <else>
         <choose>
-          <if variable="volume">
-            <text macro="issued" prefix=" (" suffix=")"/>
+          <if match="none" variable="publisher">
+            <text variable="publisher-place"/>
           </if>
           <else>
-            <text macro="issued" prefix=", "/>
+            <text variable="publisher"/>
           </else>
         </choose>
-      </if>
-      <else-if variable="publisher-place publisher" match="any">
-        <group prefix=" (" suffix=")" delimiter=", ">
-          <group delimiter=" ">
-            <choose>
-              <if variable="title" match="none"/>
-              <else-if type="thesis speech" match="any">
-                <text variable="genre" prefix="unpublished "/>
-              </else-if>
-            </choose>
-            <text macro="event"/>
-          </group>
-          <text macro="publisher"/>
-          <text macro="issued"/>
-        </group>
-      </else-if>
-      <else>
-        <text macro="issued" prefix=", "/>
       </else>
     </choose>
   </macro>
-  <macro name="locators-specific-note">
+  <macro name="issued">
     <choose>
-      <if type="bill book chapter graphic legal_case legislation motion_picture paper-conference report song" match="any">
+      <if match="any" type="report article-newspaper article-magazine personal_communication">
+        <date form="text" variable="issued"/>
+      </if>
+      <else-if match="any" type="article-journal book bill chapter legislation motion_picture paper-conference song thesis">
         <choose>
-          <if is-numeric="volume">
-            <number variable="volume" form="roman" font-variant="small-caps"/>
+          <if is-uncertain-date="issued">
+            <date date-parts="year" form="numeric" prefix="[" suffix="?]" variable="issued"/>
           </if>
           <else>
-            <text variable="volume" font-variant="small-caps"/>
+            <date date-parts="year" form="numeric" variable="issued"/>
           </else>
         </choose>
+      </else-if>
+      <else>
+        <choose>
+          <if is-uncertain-date="issued">
+            <date form="text" prefix="[" suffix="?]" variable="issued"/>
+          </if>
+          <else>
+            <date form="text" variable="issued"/>
+          </else>
+        </choose>
+      </else>
+    </choose>
+  </macro>
+  <macro name="issue-join-with-space">
+    <choose>
+      <if type="article-journal" variable="volume">
+        <text macro="issue"/>
       </if>
+      <else-if type="article-journal" variable="issue">
+        <text macro="issue"/>
+      </else-if>
+      <else-if match="any" variable="publisher-place publisher">
+        <text macro="issue"/>
+      </else-if>
     </choose>
   </macro>
-  <macro name="container-title-note">
+  <macro name="issue-join-with-comma">
     <choose>
-      <if type="chapter paper-conference" match="any">
-        <text term="in" suffix=" "/>
+      <if type="article-journal">
+        <choose>
+          <if match="none" variable="volume issue">
+            <text macro="issue"/>
+          </if>
+        </choose>
       </if>
+      <else-if match="none" variable="publisher-place publisher">
+        <text macro="issue"/>
+      </else-if>
     </choose>
-    <text variable="container-title" text-case="title" font-style="italic"/>
   </macro>
-  <macro name="edition-note">
+  <macro name="issue">
     <choose>
-      <if type="bill book chapter graphic legal_case legislation motion_picture paper-conference report song" match="any">
+      <if type="article-journal">
         <choose>
-          <if is-numeric="edition">
-            <group delimiter=" ">
-              <number variable="edition" form="ordinal"/>
-              <text term="edition" form="short"/>
-            </group>
+          <if match="any" variable="volume issue">
+            <text macro="issued" prefix="(" suffix=")"/>
           </if>
           <else>
-            <text variable="edition"/>
+            <text macro="issued"/>
           </else>
         </choose>
       </if>
+      <else-if match="any" variable="publisher-place publisher">
+        <group delimiter=", " prefix="(" suffix=")">
+          <group delimiter=" ">
+            <choose>
+              <if match="none" variable="title"/>
+              <else-if match="any" type="thesis speech">
+                <text prefix="unpublished " variable="genre"/>
+              </else-if>
+            </choose>
+            <text macro="event"/>
+          </group>
+          <text macro="publisher"/>
+          <text macro="issued"/>
+        </group>
+      </else-if>
+      <else>
+        <text macro="issued"/>
+      </else>
     </choose>
   </macro>
-  <macro name="recipient-note">
-    <names variable="recipient" delimiter=", ">
-      <label form="verb" prefix=" " suffix=" "/>
-      <name and="text" delimiter=", "/>
-    </names>
+  <macro name="artwork">
+    <choose>
+      <if match="any" type="graphic">
+        <group delimiter=", ">
+          <text variable="medium"/>
+          <text variable="dimensions"/>
+        </group>
+      </if>
+    </choose>
   </macro>
-  <macro name="recipient-short">
-    <names variable="recipient">
-      <label form="verb" prefix=" " suffix=" "/>
-      <name form="short" and="text" delimiter=", "/>
-    </names>
-  </macro>
-  <macro name="contributors-short">
-    <names variable="author">
-      <name form="short" and="text" sort-separator=", " delimiter=", "/>
-      <substitute>
-        <names variable="editor"/>
-        <names variable="translator"/>
-        <text macro="title-sort-substitute"/>
-      </substitute>
-    </names>
-    <text macro="recipient-short"/>
-  </macro>
   <macro name="locators-newspaper">
     <choose>
       <if type="article-newspaper">
@@ -313,8 +373,8 @@
             <text variable="edition"/>
             <text term="edition"/>
           </group>
-          <group>
-            <text term="section" suffix=" "/>
+          <group delimiter=" ">
+            <text term="section"/>
             <text variable="section"/>
           </group>
         </group>
@@ -321,79 +381,78 @@
       </if>
     </choose>
   </macro>
-  <macro name="event">
-    <group>
-      <text term="presented at" suffix=" "/>
-      <text variable="event"/>
+  <macro name="pages">
+    <group delimiter=" ">
+      <label form="short" variable="page"/>
+      <text variable="page"/>
     </group>
   </macro>
-  <macro name="publisher">
+  <macro name="volume-number-roman">
     <choose>
-      <if type="thesis">
-        <text variable="publisher"/>
+      <if is-numeric="volume">
+        <number font-variant="small-caps" form="roman" variable="volume"/>
       </if>
       <else>
-        <text variable="publisher"/>
+        <text font-variant="small-caps" variable="volume"/>
       </else>
     </choose>
   </macro>
-  <macro name="issued">
+  <macro name="point-locators-volume-note">
     <choose>
-      <if type="report article-newspaper article-magazine personal_communication" match="any">
-        <date variable="issued">
-          <date-part name="day" suffix=" "/>
-          <date-part name="month" suffix=" "/>
-          <date-part name="year"/>
-        </date>
+      <if position="first" type="chapter entry-dictionary entry-encyclopedia paper-conference">
+        <group delimiter=", ">
+          <text macro="volume-number-roman"/>
+          <text font-style="italic" text-case="title" variable="volume-title"/>
+        </group>
       </if>
-      <else>
-        <date variable="issued">
-          <date-part name="year"/>
-        </date>
-      </else>
-    </choose>
-  </macro>
-  <macro name="pages">
-    <choose>
-      <if type="article-journal">
-        <group delimiter=" " prefix=", ">
-          <label variable="page" form="short"/>
-          <text variable="page"/>
+      <else-if type="book legislation motion_picture report">
+        <group delimiter=", ">
+          <text macro="volume-number-roman"/>
+          <choose>
+            <if position="first">
+              <text font-style="italic" text-case="title" variable="volume-title"/>
+            </if>
+          </choose>
         </group>
-      </if>
-      <else>
-        <choose>
-          <if variable="volume">
-            <text variable="page" prefix=", "/>
-          </if>
-          <else>
-            <label variable="page" form="short" prefix=", " suffix=" "/>
-            <text variable="page"/>
-          </else>
-        </choose>
-      </else>
+      </else-if>
     </choose>
   </macro>
-  <macro name="point-locators">
-    <text macro="pages"/>
+  <macro name="point-locators-volume">
     <choose>
-      <if variable="page">
-        <group prefix=" (" suffix=")">
-          <label variable="locator" form="short" suffix=" "/>
-          <text variable="locator"/>
+      <if type="bill book chapter entry-dictionary entry-encyclopedia graphic legal_case legislation motion_picture paper-conference report song">
+        <group delimiter=", ">
+          <text macro="volume-number-roman"/>
+          <text font-style="italic" text-case="title" variable="volume-title"/>
         </group>
       </if>
-      <else>
-        <label variable="locator" form="short" prefix=", " suffix=" "/>
-        <text variable="locator"/>
-      </else>
     </choose>
   </macro>
   <macro name="point-locators-subsequent">
-    <label variable="locator" form="short" prefix=", " suffix=" "/>
-    <text variable="locator"/>
+    <group delimiter=" ">
+      <label form="short" variable="locator"/>
+      <text variable="locator"/>
+    </group>
   </macro>
-  <macro name="archive-note">
+  <macro name="point-locators">
+    <group delimiter=" ">
+      <text macro="pages"/>
+      <choose>
+        <if variable="page">
+          <group delimiter=" " prefix="(" suffix=")">
+            <label form="short" variable="locator"/>
+            <text variable="locator"/>
+          </group>
+        </if>
+        <else>
+          <group delimiter=" ">
+            <label form="short" variable="locator"/>
+            <text variable="locator"/>
+          </group>
+        </else>
+      </choose>
+    </group>
+  </macro>
+  <macro name="archive">
     <group delimiter=", ">
       <text variable="archive-place"/>
       <text variable="archive"/>
@@ -400,96 +459,106 @@
       <text variable="archive_location"/>
     </group>
   </macro>
-  <macro name="access-note">
+  <macro name="access">
     <group delimiter=", ">
       <choose>
-        <if type="article-journal bill chapter legal_case legislation paper-conference" match="none">
-          <text macro="archive-note" prefix=", "/>
+        <if match="none" type="article-journal bill chapter legal_case legislation paper-conference">
+          <text macro="archive"/>
         </if>
       </choose>
+      <choose>
+        <if variable="DOI">
+          <text prefix="doi:" variable="DOI"/>
+        </if>
+      </choose>
     </group>
+  </macro>
+  <macro name="URL">
     <choose>
-      <if variable="DOI">
-        <text variable="DOI" prefix=", doi:"/>
-      </if>
-      <else>
+      <if match="none" variable="DOI">
         <choose>
           <if variable="URL">
-            <text variable="URL" prefix=" <" suffix=">"/>
-            <group prefix=" [" suffix="]">
-              <text term="accessed"/>
-              <date variable="accessed">
-                <date-part name="day" prefix=" "/>
-                <date-part name="month" prefix=" "/>
-                <date-part name="year" prefix=" "/>
-              </date>
+            <group delimiter=" ">
+              <text prefix="<" suffix=">" variable="URL"/>
+              <choose>
+                <if match="none" variable="issued">
+                  <group delimiter=" " prefix="[" suffix="]">
+                    <text term="accessed"/>
+                    <date form="text" variable="accessed"/>
+                  </group>
+                </if>
+              </choose>
             </group>
           </if>
         </choose>
-      </else>
-    </choose>
-  </macro>
-  <macro name="artwork">
-    <choose>
-      <if type="graphic" match="any">
-        <group delimiter=", ">
-          <text variable="medium"/>
-          <text variable="dimensions"/>
-        </group>
       </if>
     </choose>
   </macro>
-  <citation et-al-min="4" et-al-use-first="1" disambiguate-add-names="true" disambiguate-add-givenname="true">
-    <layout suffix="." delimiter="; ">
+  <citation disambiguate-add-givenname="true" disambiguate-add-names="true">
+    <layout delimiter="; " suffix=".">
       <choose>
         <if position="subsequent">
           <group delimiter=", ">
             <text macro="contributors-short"/>
             <text macro="title-subsequent"/>
-            <text macro="locators-specific-note"/>
+            <text macro="point-locators-volume-note"/>
+            <text macro="point-locators-subsequent"/>
           </group>
-          <text macro="point-locators-subsequent"/>
         </if>
         <else>
-          <group delimiter=", ">
-            <text macro="contributors-note"/>
-            <text macro="title-note"/>
-            <text macro="secondary-contributors-note"/>
-            <text macro="container-title-note"/>
-            <text macro="container-contributors-note"/>
-            <text macro="collection-title"/>
-            <text macro="locators-note"/>
+          <group delimiter=" ">
+            <group delimiter=", ">
+              <group delimiter=" ">
+                <group delimiter=", ">
+                  <text macro="contributors-note"/>
+                  <text macro="title"/>
+                  <text macro="secondary-contributors"/>
+                  <text macro="container-title"/>
+                  <text macro="container-contributors"/>
+                  <text macro="locators"/>
+                </group>
+                <text macro="issue-join-with-space"/>
+              </group>
+              <text macro="issue-join-with-comma"/>
+              <text macro="point-locators-volume-note"/>
+              <text macro="locators-newspaper"/>
+              <text macro="point-locators"/>
+              <text macro="access"/>
+            </group>
+            <text macro="URL"/>
           </group>
-          <text macro="issue-note"/>
-          <text macro="locators-specific-note" prefix=", "/>
-          <text macro="locators-newspaper" prefix=", "/>
-          <text macro="point-locators"/>
-          <text macro="access-note"/>
         </else>
       </choose>
     </layout>
   </citation>
-  <bibliography hanging-indent="true" et-al-min="7" et-al-use-first="6" subsequent-author-substitute="———">
+  <bibliography delimiter-precedes-et-al="after-inverted-name" hanging-indent="true" subsequent-author-substitute="——">
     <sort>
-      <key macro="author"/>
+      <key macro="contributors"/>
       <key variable="title"/>
     </sort>
     <layout>
-      <group delimiter=", ">
-        <text macro="author"/>
-        <text macro="title-note"/>
-        <text macro="secondary-contributors-note"/>
-        <text macro="container-title-note"/>
-        <text macro="container-contributors-note"/>
-        <text macro="collection-title"/>
-        <text macro="volume"/>
+      <group delimiter=" ">
+        <group delimiter=", ">
+          <group delimiter=" ">
+            <group delimiter=", ">
+              <text macro="contributors"/>
+              <text macro="title"/>
+              <text macro="secondary-contributors"/>
+              <text macro="container-title"/>
+              <text macro="container-contributors"/>
+              <text macro="locators"/>
+            </group>
+            <text macro="issue-join-with-space"/>
+          </group>
+          <text macro="issue-join-with-comma"/>
+          <text macro="point-locators-volume"/>
+          <text macro="artwork"/>
+          <text macro="locators-newspaper"/>
+          <text macro="pages"/>
+          <text macro="access"/>
+        </group>
+        <text macro="URL"/>
       </group>
-      <text macro="issue-note"/>
-      <text macro="locators-specific-note" prefix=", "/>
-      <text macro="artwork" prefix=", "/>
-      <text macro="locators-newspaper" prefix=", "/>
-      <text macro="pages"/>
-      <text macro="access-note"/>
     </layout>
   </bibliography>
 </style>



More information about the tex-live-commits mailing list.