summaryrefslogtreecommitdiff
path: root/biblio/citation-style-language
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2022-12-26 03:03:16 +0000
committerNorbert Preining <norbert@preining.info>2022-12-26 03:03:16 +0000
commit200df2fd74322d3c7b4bb2a69e711a40265ead1b (patch)
tree9a545919fa0f041b8bac6341e8afb7b7b555ff8e /biblio/citation-style-language
parent90ef58ebc56f69220f860d466e1e21ed68ce9a55 (diff)
CTAN sync 202212260303
Diffstat (limited to 'biblio/citation-style-language')
-rw-r--r--biblio/citation-style-language/CHANGELOG.md23
-rw-r--r--biblio/citation-style-language/citation-style-language-doc.pdfbin227617 -> 228973 bytes
-rw-r--r--biblio/citation-style-language/citation-style-language-doc.tex65
-rw-r--r--biblio/citation-style-language/citation-style-language.sty354
-rw-r--r--biblio/citation-style-language/citeproc-bibtex-data.lua1552
-rw-r--r--biblio/citation-style-language/citeproc-cli.lua8
-rw-r--r--biblio/citation-style-language/citeproc-context.lua2
-rw-r--r--biblio/citation-style-language/citeproc-element.lua31
-rw-r--r--biblio/citation-style-language/citeproc-engine.lua1385
-rw-r--r--biblio/citation-style-language/citeproc-ir-node.lua29
-rw-r--r--biblio/citation-style-language/citeproc-latex-core.lua66
-rw-r--r--biblio/citation-style-language/citeproc-latex-parser.lua259
-rw-r--r--biblio/citation-style-language/citeproc-latex.lua7
-rw-r--r--biblio/citation-style-language/citeproc-node-bibliography.lua307
-rw-r--r--biblio/citation-style-language/citeproc-node-citation.lua1417
-rw-r--r--biblio/citation-style-language/citeproc-node-date.lua6
-rw-r--r--biblio/citation-style-language/citeproc-node-label.lua4
-rw-r--r--biblio/citation-style-language/citeproc-node-locale.lua4
-rw-r--r--biblio/citation-style-language/citeproc-node-names.lua14
-rw-r--r--biblio/citation-style-language/citeproc-node-style.lua365
-rw-r--r--biblio/citation-style-language/citeproc-node-text.lua25
-rw-r--r--biblio/citation-style-language/citeproc-nodes.lua8
-rw-r--r--biblio/citation-style-language/citeproc-output.lua224
-rw-r--r--biblio/citation-style-language/citeproc-util.lua53
-rw-r--r--biblio/citation-style-language/citeproc.lua2
25 files changed, 4376 insertions, 1834 deletions
diff --git a/biblio/citation-style-language/CHANGELOG.md b/biblio/citation-style-language/CHANGELOG.md
index 1f5bc98a69..3e7cfa0243 100644
--- a/biblio/citation-style-language/CHANGELOG.md
+++ b/biblio/citation-style-language/CHANGELOG.md
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [v0.3.0] - 2022-12-25
+
+### Added
+
+- Add author only command `\citeauthor`.
+- Add narrative citation commands `\textcite` and `\citet` ([#17](https://github.com/zepinglee/citeproc-lua/issues/17)).
+- Add parenthetical citation commands `\parencite` and `\citep` for compatibility.
+
+### Changed
+
+- The "-locator" suffixes are removed from the `article-locator` and `title-locator` options.
+- The LaTeX markups in citation prefix is now correctly precessed ([#33](https://github.com/zepinglee/citeproc-lua/issues/33)).
+
+### Fixed
+
+- Fix an error in checking the plurity of `number-of-pages` ([#27](https://github.com/zepinglee/citeproc-lua/issues/27)).
+- Fix an error in converting value `"2nd"` to its ordinal form ([#27](https://github.com/zepinglee/citeproc-lua/issues/27)).
+- Fix missing DOI prefix when used with `hyperref` ([#28](https://github.com/zepinglee/citeproc-lua/issues/28)).
+- Fix special characters (`#` and `%`) in URL ([#30](https://github.com/zepinglee/citeproc-lua/issues/30)).
+
## [v0.2.2] - 2022-09-23
### Fixed
@@ -63,7 +83,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Initial CTAN release.
-[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.2.2...HEAD
+[Unreleased]: https://github.com/zepinglee/citeproc-lua/compare/v0.3.0...HEAD
+[v0.3.0]: https://github.com/zepinglee/citeproc-lua/compare/v0.2.2...0.3.0
[v0.2.2]: https://github.com/zepinglee/citeproc-lua/compare/v0.2.1...0.2.2
[v0.2.1]: https://github.com/zepinglee/citeproc-lua/compare/v0.2.0...0.2.1
[v0.2.0]: https://github.com/zepinglee/citeproc-lua/compare/v0.1.1...0.2.0
diff --git a/biblio/citation-style-language/citation-style-language-doc.pdf b/biblio/citation-style-language/citation-style-language-doc.pdf
index f94a5152e5..c0bae30d9e 100644
--- a/biblio/citation-style-language/citation-style-language-doc.pdf
+++ b/biblio/citation-style-language/citation-style-language-doc.pdf
Binary files differ
diff --git a/biblio/citation-style-language/citation-style-language-doc.tex b/biblio/citation-style-language/citation-style-language-doc.tex
index 2b5bc99058..83557185c4 100644
--- a/biblio/citation-style-language/citation-style-language-doc.tex
+++ b/biblio/citation-style-language/citation-style-language-doc.tex
@@ -49,7 +49,7 @@
}%
}
-\date{2022-09-23 v0.2.2}
+\date{2022-12-25 v0.3.0}
\maketitle
@@ -290,16 +290,16 @@ An example is as follows.
\label{tab:locators}
\begin{tabular}{lll}
\toprule
- \opt{act} & \opt{folio} & \opt{section} \\
- \opt{appendix} & \opt{issue} & \opt{sub-verbo} \\
- \opt{article-locator} & \opt{line} & \opt{supplement} \\
- \opt{book} & \opt{note} & \opt{table} \\
- \opt{canon} & \opt{opus} & \opt{timestamp} \\
- \opt{chapter} & \opt{page} & \opt{title-locator} \\
- \opt{column} & \opt{paragraph} & \opt{verse} \\
- \opt{elocation} & \opt{part} & \opt{version} \\
- \opt{equation} & \opt{rule} & \opt{volume} \\
- \opt{figure} & \opt{scene} & \\
+ \opt{act} & \opt{folio} & \opt{section} \\
+ \opt{appendix} & \opt{issue} & \opt{sub-verbo} \\
+ \opt{article} & \opt{line} & \opt{supplement} \\
+ \opt{book} & \opt{note} & \opt{table} \\
+ \opt{canon} & \opt{opus} & \opt{timestamp} \\
+ \opt{chapter} & \opt{page} & \opt{title} \\
+ \opt{column} & \opt{paragraph} & \opt{verse} \\
+ \opt{elocation} & \opt{part} & \opt{version} \\
+ \opt{equation} & \opt{rule} & \opt{volume} \\
+ \opt{figure} & \opt{scene} & \\
\bottomrule
\end{tabular}
\end{table}
@@ -310,11 +310,32 @@ recommended.
If only one optional argument is provided, it is treated as \meta{postnote}.
The \meta{postnote} is used as a page locator if it consists of only digits.
-For author-date styles, there are also narrative in-text citations where the
-author names appear in running text and only dates are enclosed in parentheses
-(\cs{citet} in \pkg{natbib} pacakge or \cs{textcite} in \pkg{biblatex}).
-However such cite command in this pakcage is not available at the momment and
-it will be implemented in the next release.
+\begin{function}{\parencite,\citep}
+ \begin{syntax}
+ \cs{parencite}\oarg{options}\marg{keys}
+ \end{syntax}
+\end{function}
+
+The \cs{parencite} and \cs{citep} command are aliases of \cs{cite}.
+They are added for compatibility with \pkg{biblatex} and \pkg{natbib} packages.
+If the citation format defined in the CSL style does not have affixes,
+these commands in \pkg{citation-style-language} do not enclose the output with
+brackets, which is different from other packages.
+
+\begin{function}{\textcite,\citet}
+ \begin{syntax}
+ \cs{textcite}\oarg{options}\marg{keys}
+ \end{syntax}
+\end{function}
+
+\DescribeOption{infix}
+These commands proceduce narrative in-text citation where the author name is
+part of the running text followed by the year in parentheses.
+These commands only work with author-date styles.
+An extra option \opt{infix} can be given to specify the text inserted between
+then author and year parts. For example, “Kesey’s early work (1962)” can be
+produced by |\textcite[infix={'s early work}]{ITEM-1}|.
+By default the infix is a space.
\begin{function}{\cites}
\begin{syntax}
@@ -332,6 +353,18 @@ scanning process. The following example illustrates its usage.
\cites[prefix = {See }, page = 6]{key1}[section = 2.3]{key2}\relax [Text]
\end{LaTeXdemo}
+\begin{function}{\citeauthor}
+ \begin{syntax}
+ \cs{citeauthor}\marg{key}
+ \end{syntax}
+\end{function}
+
+This command prints the author name.
+If the orginal citation does not contain the author name (e.g., a numeric
+style), an optional |<intext>| element can be suppplied as a sibling to the
+|<citation>| and |<bibliography>| elements in the CSL style (see
+\href{https://citeproc-js.readthedocs.io/en/latest/running.html#citation-flags-with-processcitationcluster}{citeproc-js's documentation} for details).
+
\begin{function}{\nocite}
\begin{syntax}
\cs{nocite}\marg{keys}
diff --git a/biblio/citation-style-language/citation-style-language.sty b/biblio/citation-style-language/citation-style-language.sty
index 30aaefdf42..7d0daab99e 100644
--- a/biblio/citation-style-language/citation-style-language.sty
+++ b/biblio/citation-style-language/citation-style-language.sty
@@ -9,7 +9,7 @@
\RequirePackage{expl3}
\RequirePackage{xparse}
-\ProvidesExplPackage {citation-style-language} {2022-09-23} {v0.2.2}
+\ProvidesExplPackage {citation-style-language} {2022-12-25} {v0.3.0}
{Citation Style Language for LaTeX}
\RequirePackage { l3keys2e }
@@ -34,6 +34,15 @@
}
\__csl_load_check:n
+
+% Check l3build regression-test
+\bool_new:N \l__csl_regression_test_bool
+% \if_meaning:w \ASSERT \@undefined
+% \else
+% \bool_set_true:N \l__csl_regression_test_bool
+% \fi
+
+
\sys_if_engine_luatex:T
{ \lua_now:n { csl = require("citeproc-latex") } }
@@ -47,58 +56,109 @@
\DeclareDocumentCommand \cite { o o m }
{ \__csl_cite:nnn {#1} {#2} {#3} }
+\NewDocumentCommand \parencite { o o m }
+ { \__csl_cite:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \citep { o o m }
+ { \__csl_cite:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \textcite { o o m }
+ { \__csl_text_cite:nnn {#1} {#2} {#3} }
+
+\NewDocumentCommand \citet { o o m }
+ { \__csl_text_cite:nnn {#1} {#2} {#3} }
+
% \cites[⟨prenote⟩][⟨postnote⟩]{⟨key⟩}...[⟨prenote⟩][⟨postnote⟩]{⟨key⟩}
\NewDocumentCommand \cites { }
{ \__csl_cites: }
+\NewDocumentCommand \citeauthor { o o m }
+ { \__csl_cite_author:nnn {#1} {#2} {#3} }
+
\seq_new:N \l__csl_cite_keys_seq
-\seq_new:N \l__csl_cite_items_seq
+\seq_new:N \l__csl_citation_items_seq
+\prop_new:N \l__csl_citation_properties_prop
+\prop_new:N \l__csl_citation_info_prop
\cs_new:Npn \__csl_cite:nnn #1#2#3
{
\seq_clear:N \l__csl_cite_keys_seq
- \seq_clear:N \l__csl_cite_items_seq
- \__csl_process_cite_items:nnn {#1} {#2} {#3}
- \__csl_make_citation:NN \l__csl_cite_keys_seq \l__csl_cite_items_seq
+ \seq_clear:N \l__csl_citation_items_seq
+ \prop_clear:N \l__csl_citation_properties_prop
+ \__csl_process_cite_input:nnn {#1} {#2} {#3}
+ \__csl_process_citation_info:NN \l__csl_cite_keys_seq \l__csl_citation_items_seq
+ \__csl_make_citation:N \l__csl_citation_info_prop
+ }
+
+
+\cs_new:Npn \__csl_text_cite:nnn #1#2#3
+ {
+ \seq_clear:N \l__csl_cite_keys_seq
+ \seq_clear:N \l__csl_citation_items_seq
+ \prop_clear:N \l__csl_citation_properties_prop
+ \__csl_process_cite_input:nnn {#1} {#2} {#3}
+ \__csl_process_citation_info:NN \l__csl_cite_keys_seq \l__csl_citation_items_seq
+ \prop_put:Nnn \l__csl_citation_properties_prop { noteIndex } { 0 }
+ \prop_put:Nnn \l__csl_citation_properties_prop { mode } { composite }
+ % \bool_set_false:N \l__csl_note_bool
+ \__csl_make_citation:N \l__csl_citation_info_prop
}
\cs_new:Npn \__csl_cites:
{
\seq_clear:N \l__csl_cite_keys_seq
- \seq_clear:N \l__csl_cite_items_seq
+ \seq_clear:N \l__csl_citation_items_seq
+ \prop_clear:N \l__csl_citation_properties_prop
\__csl_next_cites:nnn
}
\NewDocumentCommand \__csl_next_cites:nnn { o o g }
{
\tl_if_novalue:nTF {#3}
- { \__csl_make_citation:NN \l__csl_cite_keys_seq \l__csl_cite_items_seq }
{
- \__csl_process_cite_items:nnn {#1} {#2} {#3}
+ \__csl_process_citation_info:NN \l__csl_cite_keys_seq \l__csl_citation_items_seq
+ \__csl_make_citation:N \l__csl_citation_info_prop
+ }
+ {
+ \__csl_process_cite_input:nnn {#1} {#2} {#3}
\__csl_next_cites:nnn
}
}
+\cs_new:Npn \__csl_cite_author:nnn #1#2#3
+ {
+ \seq_clear:N \l__csl_cite_keys_seq
+ \seq_clear:N \l__csl_citation_items_seq
+ \prop_clear:N \l__csl_citation_properties_prop
+ \__csl_process_cite_input:nnn {#1} {#2} {#3}
+ \__csl_process_citation_info:NN \l__csl_cite_keys_seq \l__csl_citation_items_seq
+ \prop_put:Nnn \l__csl_citation_properties_prop { noteIndex } { 0 }
+ \prop_put:Nnn \l__csl_citation_properties_prop { mode } { author-only }
+ \bool_set_false:N \l__csl_note_bool
+ \__csl_make_citation:N \l__csl_citation_info_prop
+ }
+
+
% Appends the cite key into \l__csl_cite_keys_seq and cite-items into
-% \l__csl_cite_items_seq
+% \l__csl_citation_items_seq
% #1, #2: prenote/postnote
% #3: keys
-\cs_new:Npn \__csl_process_cite_items:nnn #1#2#3
+\cs_new:Npn \__csl_process_cite_input:nnn #1#2#3
{
\tl_if_novalue:nTF {#2}
{
\tl_if_novalue:nTF {#1}
- { \__csl_process_cite_items_aux:nnn { } { } {#3} }
- { \__csl_process_cite_items_aux:nnn { } {#1} {#3} }
+ { \__csl_process_cite_input_aux:nnn { } { } {#3} }
+ { \__csl_process_cite_input_aux:nnn { } {#1} {#3} }
}
- { \__csl_process_cite_items_aux:nnn {#1} {#2} {#3} }
+ { \__csl_process_cite_input_aux:nnn {#1} {#2} {#3} }
}
-\cs_new:Npn \__csl_process_cite_items_aux:nnn #1#2#3
+\cs_new:Npn \__csl_process_cite_input_aux:nnn #1#2#3
% #1: prenote, #2: postnote, #3: keys
% Return: "{id={ITEM-1},{locator=6},...}, {id={ITEM-2},...}, ..."
{
@@ -120,7 +180,7 @@
\cs_new:Npn \__csl_process_cite_item:nnn #1#2#3
% #1: prenote, #2: postnote, #3: key
- % Save "{id={ITEM},locator={42},label={page}}" into \l__csl_cite_items_seq
+ % Save "{id={ITEM},locator={42},label={page}}" into \l__csl_citation_items_seq
{
\prop_clear:N \l__csl_cite_item_prop
\prop_put:Nnn \l__csl_cite_item_prop { id } {#3}
@@ -148,11 +208,10 @@
}
}
}
- \seq_clear:N \l_tmpa_seq
- \prop_map_inline:Nn \l__csl_cite_item_prop
- { \seq_put_right:Nn \l_tmpa_seq { ##1 = {##2} } }
- \seq_put_right:Nx \l__csl_cite_items_seq
- { { \seq_use:Nn \l_tmpa_seq { , } } }
+ \__csl_serialize_prop:NN \l__csl_cite_item_prop \l_tmpa_tl
+ \tl_put_left:NV \l_tmpa_tl { \c_left_brace_str }
+ \tl_put_right:NV \l_tmpa_tl { \c_right_brace_str }
+ \seq_put_right:NV \l__csl_citation_items_seq \l_tmpa_tl
}
\cs_new:Npn \__csl_set_locator:nn #1#2
@@ -170,9 +229,10 @@
suppress-author .prop_put:N = \l__csl_cite_item_prop,
author-only .prop_put:N = \l__csl_cite_item_prop,
uris .prop_put:N = \l__csl_cite_item_prop,
+ % Locators.
act .code:n = { \__csl_set_locator:nn { act } {#1} } ,
appendix .code:n = { \__csl_set_locator:nn { appendix } {#1} } ,
- article-locator .code:n = { \__csl_set_locator:nn { article-locator } {#1} } ,
+ article .code:n = { \__csl_set_locator:nn { article-locator } {#1} } ,
book .code:n = { \__csl_set_locator:nn { book } {#1} } ,
canon .code:n = { \__csl_set_locator:nn { canon } {#1} } ,
chapter .code:n = { \__csl_set_locator:nn { chapter } {#1} } ,
@@ -195,50 +255,74 @@
supplement .code:n = { \__csl_set_locator:nn { supplement } {#1} } ,
table .code:n = { \__csl_set_locator:nn { table } {#1} } ,
timestamp .code:n = { \__csl_set_locator:nn { timestamp } {#1} } ,
- title-locator .code:n = { \__csl_set_locator:nn { title-locator } {#1} } ,
+ title .code:n = { \__csl_set_locator:nn { title-locator } {#1} } ,
verse .code:n = { \__csl_set_locator:nn { verse } {#1} } ,
version .code:n = { \__csl_set_locator:nn { version } {#1} } ,
volume .code:n = { \__csl_set_locator:nn { volume } {#1} } ,
+ % Citation properties
+ infix .prop_put:N = \l__csl_citation_properties_prop,
}
\tl_new:N \l__csl_citation_id_tl
\tl_new:N \l__csl_cite_items_tl
\tl_new:N \l__csl_note_index_tl
+
+% Load the cite keys and prepare:
+% - \l__csl_citation_id_tl
+% - \l__csl_citation_properties_prop
+%
+% #1: \l__csl_cite_keys_seq
+% #2: \l__csl_citation_items_seq
+\cs_new:Npn \__csl_process_citation_info:NN #1#2
+ {
+ \__csl_process_citation_id:NN \l__csl_citation_id_tl #1
+ \__csl_get_note_index:N \l__csl_note_index_tl
+ \prop_put:NnV \l__csl_citation_properties_prop { noteIndex } \l__csl_note_index_tl
+ }
+
+
\tl_new:N \l__csl_citation_info_tl
\tl_new:N \l__csl_citation_tl
\prop_new:N \g__csl_citations_prop
-\prop_clear:N \l__csl_citation_prop
-\cs_new:Npn \__csl_make_citation:NN #1#2
- % #1: \l__csl_cite_keys_seq
- % #2: \l__csl_cite_items_seq
+\tl_new:N \l__csl_citation_properties_tl
+
+% Write citation info to aux and print the citation contents.
+% #1: \l__csl_citation_info_prop
+\cs_new:Npn \__csl_make_citation:N #1
{
- \prop_clear:N \l__csl_citation_prop
- \__csl_process_citation_id:NN \l__csl_citation_id_tl #1
- \prop_put:NnV \l__csl_citation_prop { citationID } \l__csl_citation_id_tl
- \tl_set:Nx \l__csl_cite_items_tl
- { \seq_use:Nn #2 { , } }
- \prop_put:NnV \l__csl_citation_prop { citationItems } \l__csl_cite_items_tl
- \__csl_get_note_index:N \l__csl_note_index_tl
- \prop_put:Nnx \l__csl_citation_prop { properties } { noteIndex = { \l__csl_note_index_tl } }
- \tl_clear:N \l__csl_citation_info_tl
- \prop_map_inline:Nn \l__csl_citation_prop
- {
- \tl_if_empty:NF \l__csl_citation_info_tl
- { \tl_put_right:Nn \l__csl_citation_info_tl { , } }
- \tl_put_right:Nn \l__csl_citation_info_tl { ##1 = { ##2 } }
- }
+ \prop_clear:N \l__csl_citation_info_prop
+ % citationID
+ \prop_put:NnV \l__csl_citation_info_prop { citationID } \l__csl_citation_id_tl
+ % citationItems
+ \__csl_serialize_seq:NN \l__csl_citation_items_seq \l__csl_cite_items_tl
+ \prop_put:NnV \l__csl_citation_info_prop { citationItems } \l__csl_cite_items_tl
+ % properties
+ \__csl_serialize_prop:NN \l__csl_citation_properties_prop \l__csl_citation_properties_tl
+ \prop_put:NnV \l__csl_citation_info_prop { properties } \l__csl_citation_properties_tl
+ \__csl_serialize_prop:NN \l__csl_citation_info_prop \l__csl_citation_info_tl
+ % Write to .aux file
+ % \tl_show:N \l__csl_citation_info_tl
\exp_args:NV \__csl_write_aux_citation:n \l__csl_citation_info_tl
+ \bool_if:NT \l__csl_regression_test_bool
+ { \tl_show:N \l__csl_citation_info_tl }
+ % Print the citation string
\prop_get:NVNTF \g__csl_citations_prop \l__csl_citation_id_tl
\l__csl_citation_tl
- { \exp_args:NV \__csl_print_citation:n \l__csl_citation_tl }
+ { \__csl_print_citation:N \l__csl_citation_tl }
{
\bool_if:NTF \l__csl_engine_initialized_bool
{
- \tl_set:Nf \l__csl_citation_tl
- { \lua_now:e { csl.cite("\l__csl_citation_info_tl") } }
- \exp_args:NV \__csl_print_citation:n \l__csl_citation_tl
+ % \tl_show:N \l__csl_citation_info_tl
+ % \tl_set:Nf \l__csl_citation_tl
+ % { \exp_args:NV \__csl_cite_aux:n \l__csl_citation_info_tl }
+ \group_begin:
+ \char_set_catcode_other:N \%
+ \char_set_catcode_other:N \#
+ \exp_args:NV \__csl_cite_aux:n \l__csl_citation_info_tl
+ \__csl_print_citation:N \l__csl_citation_tl
+ \group_end:
}
{
\exp_args:Nx \__csl_print_undefined_citation:n
@@ -247,6 +331,34 @@
}
}
+\cs_new:Npn \__csl_cite_aux:n #1
+ { \lua_now:e { csl.cite("\lua_escape:n {#1}") } }
+
+% #1: seq
+% #2: tl
+\cs_new:Npn \__csl_serialize_seq:NN #1#2
+ {
+ \tl_clear:N #2
+ \seq_map_inline:Nn #1
+ {
+ \tl_if_empty:NF #2
+ { \tl_put_right:Nn #2 { , } }
+ \tl_put_right:Nn #2 { ##1 }
+ }
+ }
+
+% #1: prop
+% #2: tl
+\cs_new:Npn \__csl_serialize_prop:NN #1#2
+ {
+ \tl_clear:N #2
+ \prop_map_inline:Nn #1
+ {
+ \tl_if_empty:NF #2
+ { \tl_put_right:Nn #2 { , } }
+ \tl_put_right:Nn #2 { ##1 = { ##2 } }
+ }
+ }
\tl_new:N \l__csl_cite_keys_tl
\tl_new:N \l__csl_citation_count_tl
@@ -260,6 +372,8 @@
{
\tl_set:Nx \l__csl_cite_keys_tl
{ \seq_use:Nn #2 { , } }
+ % \prop_show:N \g__csl_citations_count_prop
+ % \tl_show:N \l__csl_cite_keys_tl
\prop_get:NVNTF \g__csl_citations_count_prop \l__csl_cite_keys_tl
\l__csl_citation_count_tl
{
@@ -274,18 +388,31 @@
}
+\int_new:N \g__csl_pseudo_note_index_int
+\int_gset:Nn \g__csl_pseudo_note_index_int { 0 }
+
% Save the note number to \l__csl_note_index_tl
% TODO: multiple citations in a note
\cs_new:Npn \__csl_get_note_index:N #1
% #1: \l__csl_note_index_tl
{
- \bool_if:NTF \l__csl_note_style_bool
+ \bool_if:NTF \l__csl_note_bool
{
\int_set_eq:Nc \l_tmpa_int { c@ \@mpfn }
\int_incr:N \l_tmpa_int
\tl_set:Nx #1 { \int_use:N \l_tmpa_int }
}
- { \tl_set:Nn #1 { 0 } }
+ {
+ \tl_if_empty:NTF \l__csl_class_tl
+ {
+ % The style class (in-text/note) is undetermined.
+ \int_set_eq:Nc \l_tmpa_int { c@ \@mpfn }
+ \int_gincr:N \g__csl_pseudo_note_index_int
+ \int_add:Nn \l_tmpa_int { \g__csl_pseudo_note_index_int }
+ \tl_set:Nx #1 { \int_use:N \l_tmpa_int }
+ }
+ { \tl_set:Nx #1 { 0 } }
+ }
}
@@ -293,43 +420,30 @@
% #1: citation info "{<citationID>}{{id=ITEM-1},{id=ITEM-2}}{<noteIndex>}"
{
\if@filesw
- \iow_now:Nx \@auxout
- { \token_to_str:N \citation { #1 } }
+ \iow_now:Nn \@auxout { \citation {#1} }
\fi
}
-% \cs_new:Npn \__csl_print_citation:n #1
-% % #1: citation text
-% {
-% \bool_if:NTF \l__csl_note_style_bool
-% {
-% \footnote {#1}
-% }
-% {#1}
-% }
-
-\cs_new:Npn \__csl_print_citation:n #1
- % #1: "{<type>}{<citation text>}"
- { \__csl_print_citation_aux:nn #1 }
-
-\cs_new:Npn \__csl_print_citation_aux:nn #1#2
- % #1: citation type, "in-text" or "note"
- % #2: citation text
+% #1: \l__csl_citation_tl
+\cs_new:Npn \__csl_print_citation:N #1
{
- \tl_set:Nn \l__csl_citation_tl {#2}
- \tl_if_eq:nnTF {#1} { note }
- { \footnote {#2} }
- {#2}
+ \bool_if:NT \l__csl_regression_test_bool
+ { \tl_show:N #1 }
+ \bool_if:NTF \l__csl_note_bool
+ { \footnote {#1} }
+ {#1}
}
\cs_new:Npn \__csl_print_undefined_citation:n #1
% #1: keys
{
- \tl_set:Nn \l__csl_citation_tl { [ \textbf {#1} ] }
\G@refundefinedtrue
\msg_warning:nnn { citation-style-language } { citation / undefined } {#1}
+ \tl_set:Nn \l__csl_citation_tl { [ \textbf {#1} ] }
+ \bool_if:NT \l__csl_regression_test_bool
+ { \tl_show:N \l__csl_citation_tl }
\group_begin:
\reset@font [ \textbf {#1} ]
\group_end:
@@ -346,19 +460,24 @@
\cs_new:Npn \__csl_no_cite:n #1
{
\seq_clear:N \l__csl_cite_keys_seq
- \seq_clear:N \l__csl_cite_items_seq
- \__csl_process_cite_items:nnn { } { } {#1}
+ \seq_clear:N \l__csl_citation_items_seq
+ \__csl_process_cite_input:nnn { } { } {#1}
\tl_set:Nx \l__csl_cite_items_tl
- { \seq_use:Nn \l__csl_cite_items_seq { , } }
+ { \seq_use:Nn \l__csl_citation_items_seq { , } }
\tl_set:Nx \l__csl_citation_info_tl
{
citationID = { @nocite } ,
citationItems = { \l__csl_cite_items_tl } ,
properties = { noteIndex = { 0 } }
}
+ \bool_if:NT \l__csl_regression_test_bool
+ { \tl_show:N \l__csl_citation_info_tl }
\exp_args:NV \__csl_no_cite_write_aux:n \l__csl_citation_info_tl
\sys_if_engine_luatex:T
{ \lua_now:n { csl.nocite("#1") } }
+ \tl_clear:N \l__csl_citation_tl
+ \bool_if:NT \l__csl_regression_test_bool
+ { \tl_show:N \l__csl_citation_tl }
}
@@ -369,9 +488,7 @@
\AtBeginDocument
{ \exp_args:Nx \__csl_write_aux_citation:n { #1 } }
}
- {
- \exp_args:Nx \__csl_write_aux_citation:n { #1 }
- }
+ { \exp_args:Nx \__csl_write_aux_citation:n { #1 } }
}
@@ -396,10 +513,10 @@
\cs_new:Npn \cslcite #1#2
{
- \bibcite {#1} {#2}
% \if@filesw
- % \iow_now:Nx \@auxout { \token_to_str:N \bibcite {#1} {#2} }
+ % \iow_now:Nn \@auxout { \bibcite {#1} {#2} }
% \fi
+ \bibcite {#1} {#2}
}
@@ -411,22 +528,22 @@
\NewDocumentCommand \printbibliography { O { } }
{
- \sys_if_engine_luatex:TF
+ \sys_if_engine_luatex:T
{
- \bool_if:NTF \l__csl_engine_initialized_bool
+ \bool_if:NT \l__csl_engine_initialized_bool
{
\__csl_collect_bibliography:n { \lua_now:n { csl.bibliography() } }
- % \tl_show:N \g__csl_bibliography_tl
- \tl_use:N \g__csl_bibliography_tl
}
- { \msg_warning:nnn { citation-style-language } { bibliography / empty } }
}
+ \tl_if_empty:NTF \g__csl_bibliography_tl
+ { \msg_warning:nn { citation-style-language } { bibliography / empty } }
{
- \tl_if_empty:NTF \g__csl_bibliography_tl
- { \msg_warning:nnn { citation-style-language } { bibliography / empty } }
+ \bool_if:NT \l__csl_regression_test_bool
{
- \tl_use:N \g__csl_bibliography_tl
+ \seq_set_split:NnV \l__csl_bibliography_seq { \par } \g__csl_bibliography_tl
+ \seq_show:N \l__csl_bibliography_seq
}
+ \tl_use:N \g__csl_bibliography_tl
}
}
@@ -435,6 +552,8 @@
\tl_new:N \l__csl_style_tl
+\tl_new:N \l__csl_class_tl
+\bool_new:N \l__csl_note_bool
\clist_new:N \l__csl_bib_resources_clist
\tl_new:N \l__csl_prefix_separator_tl
\tl_new:N \l__csl_suffix_separator_tl
@@ -449,7 +568,14 @@
\keys_define:nn { csl }
{
+ regression-test .bool_set:N = \l__csl_regression_test_bool ,
style .tl_set:N = \l__csl_style_tl ,
+ class .code:n =
+ {
+ \tl_set:Nn \l__csl_class_tl {#1}
+ \tl_if_eq:NnT \l__csl_class_tl { note }
+ { \bool_set_true:N \l__csl_note_bool }
+ } ,
prefix-separator .tl_set:N = \l__csl_prefix_separator_tl ,
suffix-separator .tl_set:N = \l__csl_suffix_separator_tl ,
locale .tl_set:N = \l__csl_locale_tl ,
@@ -593,6 +719,7 @@
\tl_new:N \g__csl_bibliography_tl
\tl_new:N \g__csl_bibliography_setup_tl
+\seq_new:N \l__csl_bibliography_seq
\AtBeginDocument { \__csl_at_begin_document_hook: }
@@ -602,12 +729,8 @@
{
\__csl_write_aux_info:
\sys_if_engine_luatex:TF
- {
- \__csl_initialize_lua_module:
- }
- {
- \__csl_load_bbl:
- }
+ { \__csl_initialize_lua_module: }
+ { \__csl_load_bbl: }
}
@@ -652,16 +775,16 @@
}
-\str_new:N \l__csl_style_class_str
+% \str_new:N \l__csl_style_class_str
% In-text (including numeric or author-date) or note style
-\bool_new:N \l__csl_note_style_bool
+% \bool_new:N \l__csl_note_style_bool
\cs_new:Npn \__csl_get_style_class: {
\bool_if:NT \l__csl_engine_initialized_bool
{
- \str_set:Nx \l__csl_style_class_str { \lua_now:n { csl.get_style_class() } }
- \str_if_eq:VnT \l__csl_style_class_str { note }
- { \bool_set_true:N \l__csl_note_style_bool }
+ \tl_set:Nx \l__csl_class_tl { \lua_now:n { csl.get_style_class() } }
+ \tl_if_eq:NnT \l__csl_class_tl { note }
+ { \bool_set_true:N \l__csl_note_bool }
}
}
@@ -692,16 +815,18 @@
% write to aux file
\prop_if_empty:NF \l__csl_options_clist
{
- \if@filesw
- \iow_now:Nx \@auxout
- {
- \token_to_str:N \csloptions
- { \clist_use:Nn \l__csl_options_clist { , } }
- }
- \fi
+ \exp_args:Nx \__csl_write_aux_options:n
+ { \clist_use:Nn \l__csl_options_clist { , } }
}
}
+\cs_new:Npn \__csl_write_aux_options:n #1
+ {
+ \if@filesw
+ \iow_now:Nn \@auxout { \csloptions {#1} }
+ \fi
+ }
+
% Load .bbl at the beginning of document to save one pass of latex.
% In this procedure, the \cslcite command is processed and the contents
@@ -711,16 +836,19 @@
% The \@input@ prints "No file ....bbl" in the .log file from which
% the latexmk decides to run $bibtex or not.
\__csl_collect_bibliography:n { \@input@ { \jobname .bbl } }
- % \file_if_exist_input:nF { \jobname .bbl }
- % {
- % \msg_warning:nnx { citation-style-language } { file / non-exist} { \jobname .bbl }
- % }
+ % Execute the \cslsetup part to set the style class.
+ % \tl_show:N \g__csl_bibliography_setup_tl
+ \tl_use:N \g__csl_bibliography_setup_tl
+ % \bool_show:N \l__csl_note_bool
}
% Collection the bibliography (as well as \cslsetup) into \g__csl_bibliography_setup_tl
\cs_new:Npn \__csl_collect_bibliography:n #1
{
\group_begin:
+ % URLs may contain "%" and "#" characters.
+ \char_set_catcode_other:N \%
+ \char_set_catcode_other:N \#
\RenewDocumentCommand \cslsetup { m }
{ \tl_gset:Nn \g__csl_bibliography_setup_tl { \cslsetup { ##1 } } }
\RenewDocumentEnvironment { thebibliography } { m +b }
@@ -780,6 +908,7 @@
\cs_new:Npn \__csl_write_aux_bibdata:n #1
{
\if@filesw
+ % Full expansion for files like \jobname.bib
\iow_now:Nx \@auxout { \token_to_str:N \csl@data {#1} }
\fi
}
@@ -1043,7 +1172,7 @@
{
\l__csl_bib_postamble_tl
\tl_set:Nn \@noitemerr
- { \msg_warning:nnn { citation-style-language } { bibliography / empty } }
+ { \msg_warning:nn { citation-style-language } { bibliography / empty } }
\endlist
\__csl_reset_bib_format:
}
@@ -1077,9 +1206,8 @@
{
\BlockquoteDisable
{
- \cs_set_eq:NN \__csl_make_citation:NN \use_none:nn
- % \sys_if_engine_luatex:T
- % { \lua_now:n { csl.preview_mode = true } }
+ \cs_set_eq:NN \__csl_process_citation_info:NN \use_none:nn
+ \cs_set_eq:NN \__csl_make_citation:N \use_none:n
}
}
diff --git a/biblio/citation-style-language/citeproc-bibtex-data.lua b/biblio/citation-style-language/citeproc-bibtex-data.lua
index c7b2ff1306..e5b4aa5a78 100644
--- a/biblio/citation-style-language/citeproc-bibtex-data.lua
+++ b/biblio/citation-style-language/citeproc-bibtex-data.lua
@@ -3,6 +3,12 @@
return {
description = "BibTeX CSL mapping",
types = {
+ abstract = {
+ csl = nil,
+ },
+ ancienttext = {
+ csl = "classic",
+ },
archival = {
csl = "collection",
},
@@ -12,6 +18,15 @@ return {
article = {
csl = "article-journal",
},
+ ["article-journal"] = {
+ csl = "article-journal",
+ },
+ ["article-magazine"] = {
+ csl = "article-magazine",
+ },
+ ["article-newspaper"] = {
+ csl = "article-newspaper",
+ },
artifactdataset = {
csl = "dataset",
},
@@ -33,21 +48,81 @@ return {
bibnote = {
csl = nil,
},
+ bill = {
+ csl = "bill",
+ },
book = {
csl = "book",
},
+ bookinarticle = {
+ csl = nil,
+ },
bookinbook = {
csl = "chapter",
},
+ bookincollection = {
+ csl = nil,
+ },
+ bookininarticle = {
+ csl = nil,
+ },
+ bookininbook = {
+ csl = nil,
+ },
+ bookinincollection = {
+ csl = nil,
+ },
+ bookininproceedings = {
+ csl = nil,
+ },
+ bookininreference = {
+ csl = nil,
+ },
+ bookinjournal = {
+ csl = nil,
+ },
+ bookinmathesis = {
+ csl = nil,
+ },
+ bookinphdthesis = {
+ csl = nil,
+ },
+ bookinproceedings = {
+ csl = nil,
+ },
+ bookinreference = {
+ csl = nil,
+ },
+ bookinthesis = {
+ csl = nil,
+ },
booklet = {
csl = "pamphlet",
},
+ broadcast = {
+ csl = "broadcast",
+ },
brochure = {
csl = "pamphlet",
},
- cconference = {
+ catalog = {
csl = nil,
},
+ cconference = {
+ csl = "paper-conference",
+ },
+ chapter = {
+ csl = "chapter",
+ },
+ classic = {
+ csl = "classic",
+ },
+ classictext = {
+ csl = "classic",
+ },
+ codefragment = {
+ csl = "software",
+ },
collection = {
csl = "book",
},
@@ -60,9 +135,18 @@ return {
commented = {
csl = nil,
},
+ committee = {
+ csl = nil,
+ },
conference = {
csl = "paper-conference",
},
+ conferencepaper = {
+ csl = "paper-conference",
+ },
+ constitution = {
+ csl = "legislation",
+ },
ctan = {
csl = "software",
},
@@ -84,54 +168,138 @@ return {
customf = {
csl = nil,
},
+ data = {
+ csl = nil,
+ },
database = {
csl = "dataset",
},
dataset = {
csl = "dataset",
},
+ degree = {
+ csl = nil,
+ },
dictionary = {
csl = "book",
},
docthesis = {
csl = "thesis",
},
+ document = {
+ csl = "document",
+ },
electronic = {
csl = "webpage",
},
+ entry = {
+ csl = "entry",
+ },
+ ["entry-dictionary"] = {
+ csl = "entry-dictionary",
+ },
+ ["entry-encyclopedia"] = {
+ csl = "entry-encyclopedia",
+ },
eulegislation = {
csl = "legislation",
},
+ event = {
+ csl = "event",
+ },
+ exhibcatalog = {
+ csl = nil,
+ },
+ figure = {
+ csl = "figure",
+ },
footnote = {
csl = nil,
},
+ funding = {
+ csl = nil,
+ },
game = {
csl = "software",
},
+ gitcommit = {
+ csl = nil,
+ },
govpub = {
csl = "regulation",
},
+ graphic = {
+ csl = "graphic",
+ },
habthesis = {
csl = "thesis",
},
heading = {
csl = nil,
},
+ hearing = {
+ csl = "hearing",
+ },
hidden = {
csl = nil,
},
image = {
csl = "graphic",
},
+ inarticle = {
+ csl = nil,
+ },
+ inaudio = {
+ csl = nil,
+ },
inbook = {
csl = "chapter",
},
+ incatalog = {
+ csl = nil,
+ },
incollection = {
csl = "chapter",
},
+ incommentary = {
+ csl = nil,
+ },
+ inexhibcatalog = {
+ csl = nil,
+ },
+ ininarticle = {
+ csl = nil,
+ },
+ ininbook = {
+ csl = nil,
+ },
+ inincollection = {
+ csl = nil,
+ },
+ ininproceedings = {
+ csl = nil,
+ },
+ ininreference = {
+ csl = nil,
+ },
+ inlexicon = {
+ csl = nil,
+ },
inloosecollection = {
csl = "chapter",
},
+ inmathesis = {
+ csl = nil,
+ },
+ inmovie = {
+ csl = nil,
+ },
+ inmusic = {
+ csl = nil,
+ },
+ inphdthesis = {
+ csl = nil,
+ },
inpress = {
csl = "article",
},
@@ -147,6 +315,15 @@ return {
internet = {
csl = "webpage",
},
+ interview = {
+ csl = "interview",
+ },
+ inthesis = {
+ csl = nil,
+ },
+ invideo = {
+ csl = nil,
+ },
journalpart = {
csl = nil,
},
@@ -154,20 +331,32 @@ return {
csl = "periodical",
},
jurisdiction = {
- csl = nil,
+ csl = "legal_case",
},
jurthesis = {
csl = "thesis",
},
+ legadminmaterial = {
+ csl = "regulation",
+ },
legal = {
csl = "treaty",
},
+ legal_case = {
+ csl = "legal_case",
+ },
legislation = {
csl = "legislation",
},
+ legmaterial = {
+ csl = "legislation",
+ },
letter = {
csl = "personal_communication",
},
+ letters = {
+ csl = "personal_communication",
+ },
lexicon = {
csl = "book",
},
@@ -177,6 +366,9 @@ return {
manual = {
csl = "report",
},
+ manuscript = {
+ csl = "manuscript",
+ },
map = {
csl = "map",
},
@@ -192,33 +384,54 @@ return {
misc = {
csl = "document",
},
+ mlasource = {
+ csl = nil,
+ },
monograph = {
csl = "book",
},
monography = {
csl = "book",
},
+ motion_picture = {
+ csl = "motion_picture",
+ },
movie = {
csl = "motion_picture",
},
music = {
csl = "song",
},
+ musical_score = {
+ csl = "musical_score",
+ },
mvbook = {
csl = "book",
},
mvcollection = {
csl = "book",
},
+ mvcommentary = {
+ csl = nil,
+ },
+ mvlexicon = {
+ csl = nil,
+ },
mvproceedings = {
csl = "book",
},
mvreference = {
csl = "book",
},
+ nameonly = {
+ csl = nil,
+ },
news = {
csl = "article-newspaper",
},
+ newsarticle = {
+ csl = "article-newspaper",
+ },
newspaper = {
csl = "article-newspaper",
},
@@ -228,6 +441,12 @@ return {
other = {
csl = nil,
},
+ pamphlet = {
+ csl = "pamphlet",
+ },
+ ["paper-conference"] = {
+ csl = "paper-conference",
+ },
patent = {
csl = "patent",
},
@@ -237,9 +456,18 @@ return {
periodical = {
csl = "periodical",
},
+ personal_communication = {
+ csl = "personal_communication",
+ },
phdthesis = {
csl = "thesis",
},
+ post = {
+ csl = "post",
+ },
+ ["post-weblog"] = {
+ csl = "post-weblog",
+ },
preamble = {
csl = nil,
},
@@ -258,6 +486,9 @@ return {
reference = {
csl = "book",
},
+ regulation = {
+ csl = "regulation",
+ },
report = {
csl = "report",
},
@@ -267,6 +498,15 @@ return {
review = {
csl = "review",
},
+ ["review-book"] = {
+ csl = "review-book",
+ },
+ school = {
+ csl = nil,
+ },
+ seminarpaper = {
+ csl = nil,
+ },
set = {
csl = nil,
},
@@ -276,12 +516,27 @@ return {
software = {
csl = "software",
},
+ softwaremodule = {
+ csl = "software",
+ },
+ softwareversion = {
+ csl = nil,
+ },
+ song = {
+ csl = "song",
+ },
+ speech = {
+ csl = "speech",
+ },
standard = {
csl = "standard",
},
string = {
csl = nil,
},
+ student = {
+ csl = nil,
+ },
suppbook = {
csl = "chapter",
},
@@ -291,6 +546,12 @@ return {
suppperiodical = {
csl = "article",
},
+ talk = {
+ csl = "speech",
+ },
+ teaching = {
+ csl = nil,
+ },
techreport = {
csl = "report",
},
@@ -300,6 +561,9 @@ return {
thesis = {
csl = "thesis",
},
+ treaty = {
+ csl = "treaty",
+ },
uklegislation = {
csl = "legislation",
},
@@ -313,7 +577,7 @@ return {
csl = "webpage",
},
website = {
- csl = nil,
+ csl = "webpage",
},
www = {
csl = "webpage",
@@ -327,11 +591,27 @@ return {
csl = "abstract",
type = "literal",
},
+ abstractloc = {
+ csl = nil,
+ type = "literal",
+ },
+ abstracturl = {
+ csl = nil,
+ type = "verbatim",
+ },
+ accessed = {
+ csl = "accessed",
+ },
account = {
csl = nil,
},
acronym = {
csl = nil,
+ type = "literal",
+ },
+ actor = {
+ csl = "performer",
+ type = "name",
},
add = {
csl = nil,
@@ -352,11 +632,28 @@ return {
},
advisor = {
csl = nil,
+ type = "name",
},
afterword = {
csl = nil,
type = "name",
},
+ alternatetitle = {
+ csl = nil,
+ type = "literal",
+ },
+ alternatetitlescript = {
+ csl = nil,
+ type = "literal",
+ },
+ amendment = {
+ csl = nil,
+ type = "literal",
+ },
+ amount = {
+ csl = nil,
+ type = "integer",
+ },
annotate = {
csl = "note",
},
@@ -375,12 +672,33 @@ return {
annotelanguage = {
csl = nil,
},
+ anonstring = {
+ csl = nil,
+ type = "name",
+ },
+ appentry = {
+ csl = nil,
+ type = "literal",
+ },
applicant = {
csl = nil,
},
+ arachne = {
+ csl = nil,
+ type = "verbatim",
+ },
archive = {
csl = "archive",
},
+ ["archive-place"] = {
+ csl = "archive-place",
+ },
+ archive_collection = {
+ csl = "archive_collection",
+ },
+ archive_location = {
+ csl = "archive_location",
+ },
archiveprefix = {
csl = "archive",
type = "literal",
@@ -388,8 +706,21 @@ return {
archname = {
csl = "archive",
},
+ arthist = {
+ csl = nil,
+ type = "integer",
+ },
+ arthistdate = {
+ csl = nil,
+ type = "date",
+ },
+ article = {
+ csl = nil,
+ type = "literal",
+ },
articleno = {
csl = nil,
+ type = "literal",
},
arxiv = {
csl = nil,
@@ -401,16 +732,27 @@ return {
csl = "author",
type = "name",
},
+ authoraddon = {
+ csl = nil,
+ type = "name",
+ },
authorcountry = {
csl = nil,
+ type = "literal",
},
authorfa = {
csl = nil,
},
+ authority = {
+ csl = "authority",
+ },
authortype = {
csl = nil,
type = "key",
},
+ ["available-date"] = {
+ csl = "available-date",
+ },
bibsource = {
csl = nil,
},
@@ -426,16 +768,92 @@ return {
birthyear = {
csl = nil,
},
+ blogsubtitle = {
+ csl = nil,
+ type = "literal",
+ },
+ blogtitle = {
+ csl = "title",
+ type = "literal",
+ },
+ blogtitleaddon = {
+ csl = nil,
+ type = "literal",
+ },
+ book = {
+ csl = nil,
+ type = "range",
+ },
bookaddress = {
+ csl = "publisher-place",
+ },
+ bookafterword = {
csl = nil,
+ type = "name",
+ },
+ bookannotator = {
+ csl = nil,
+ type = "name",
},
bookauthor = {
csl = "container-author",
type = "name",
},
+ bookauthortype = {
+ csl = nil,
+ type = "literal",
+ },
+ bookbooksubtitle = {
+ csl = nil,
+ type = "literal",
+ },
+ bookbooktitle = {
+ csl = nil,
+ type = "literal",
+ },
+ bookbooktitleaddon = {
+ csl = nil,
+ type = "literal",
+ },
+ bookbooktitlekey = {
+ csl = nil,
+ type = "key",
+ },
+ bookbookvolume = {
+ csl = nil,
+ type = "literal",
+ },
+ bookcommentator = {
+ csl = nil,
+ type = "name",
+ },
+ bookcredits = {
+ csl = nil,
+ type = "literal",
+ },
+ bookeditor = {
+ csl = "editor",
+ type = "name",
+ },
+ bookforeword = {
+ csl = nil,
+ type = "name",
+ },
+ bookineditor = {
+ csl = nil,
+ type = "name",
+ },
+ bookintroduction = {
+ csl = nil,
+ type = "name",
+ },
booklanguage = {
csl = nil,
},
+ bookoriglanguage = {
+ csl = nil,
+ type = "key",
+ },
bookpages = {
csl = nil,
},
@@ -443,6 +861,10 @@ return {
csl = nil,
type = "key",
},
+ books = {
+ csl = nil,
+ type = "literal",
+ },
booksubtitle = {
csl = nil,
type = "literal",
@@ -455,6 +877,29 @@ return {
csl = nil,
type = "literal",
},
+ booktitlekey = {
+ csl = nil,
+ type = "key",
+ },
+ booktitlenote = {
+ csl = nil,
+ type = "literal",
+ },
+ booktranslator = {
+ csl = "translator",
+ type = "name",
+ },
+ bookvolume = {
+ csl = nil,
+ type = "literal",
+ },
+ bookyear = {
+ csl = nil,
+ type = "literal",
+ },
+ ["call-number"] = {
+ csl = "call-number",
+ },
caption = {
csl = nil,
},
@@ -467,6 +912,10 @@ return {
casenumber = {
csl = nil,
},
+ catalog = {
+ csl = nil,
+ type = "literal",
+ },
category = {
csl = nil,
},
@@ -474,18 +923,37 @@ return {
csl = nil,
},
chair = {
- csl = nil,
+ csl = "chair",
},
chapter = {
csl = "chapter-number",
type = "literal",
},
+ ["chapter-number"] = {
+ csl = "chapter-number",
+ },
+ citation = {
+ csl = nil,
+ type = "literal",
+ },
citedate = {
csl = nil,
},
city = {
csl = nil,
},
+ classes = {
+ csl = nil,
+ type = "entrykey",
+ },
+ claves = {
+ csl = nil,
+ type = "literal",
+ },
+ claves_definition = {
+ csl = nil,
+ type = "literal",
+ },
code = {
csl = nil,
},
@@ -494,10 +962,28 @@ return {
},
collaboration = {
csl = nil,
+ type = "literal",
},
collator = {
csl = nil,
},
+ collection = {
+ csl = nil,
+ type = "literal",
+ },
+ ["collection-editor"] = {
+ csl = "collection-editor",
+ },
+ ["collection-number"] = {
+ csl = "collection-number",
+ },
+ ["collection-title"] = {
+ csl = "collection-title",
+ },
+ columns = {
+ csl = nil,
+ type = "literal",
+ },
commentator = {
csl = nil,
type = "name",
@@ -505,8 +991,23 @@ return {
commit = {
csl = nil,
},
+ commithash = {
+ csl = nil,
+ type = "literal",
+ },
+ committee = {
+ csl = nil,
+ type = "name",
+ },
compiler = {
+ csl = "compiler",
+ },
+ composer = {
+ csl = "composer",
+ },
+ concentration = {
csl = nil,
+ type = "literal",
},
condition = {
csl = nil,
@@ -520,49 +1021,88 @@ return {
["conference-year"] = {
csl = nil,
},
+ ["container-author"] = {
+ csl = "container-author",
+ },
+ ["container-title"] = {
+ csl = "container-title",
+ },
+ ["container-title-short"] = {
+ csl = "container-title-short",
+ },
+ contents = {
+ csl = nil,
+ type = "literal",
+ },
+ contributor = {
+ csl = "contributor",
+ },
+ contributora = {
+ csl = nil,
+ type = "literal",
+ },
+ contributorb = {
+ csl = nil,
+ type = "literal",
+ },
+ copublisher = {
+ csl = nil,
+ type = "literal",
+ },
copy = {
csl = nil,
},
country = {
- csl = nil,
+ csl = "jurisdiction",
},
credits = {
csl = nil,
+ type = "literal",
},
crossref = {
csl = nil,
type = "entrykey",
},
- ["ctrl-article-title"] = {
- csl = nil,
+ curator = {
+ csl = "curator",
},
- ["ctrl-chapter-title"] = {
+ currency = {
csl = nil,
+ type = "literal",
},
- ["ctrl-doi"] = {
- csl = nil,
+ custom = {
+ csl = "custom",
},
- ["ctrl-etal-firstonly"] = {
+ dataset = {
csl = nil,
},
- ["ctrl-etal-number"] = {
+ date = {
+ csl = "issued",
+ type = "date",
+ },
+ ["date+an"] = {
csl = nil,
+ type = "literal",
},
- ["ctrl-link-doi"] = {
+ datea = {
csl = nil,
+ type = "literal",
},
- ["ctrl-use-doi-all"] = {
+ dateaddon = {
csl = nil,
+ type = "literal",
},
- ["ctrl-use-title"] = {
+ dateb = {
csl = nil,
+ type = "literal",
},
- dataset = {
+ datetype = {
csl = nil,
+ type = "literal",
},
- date = {
- csl = "issued",
- type = "date",
+ dating = {
+ csl = nil,
+ type = "literal",
},
day = {
csl = nil,
@@ -570,11 +1110,28 @@ return {
dayfiled = {
csl = nil,
},
+ decision = {
+ csl = nil,
+ type = "literal",
+ },
+ decisionname = {
+ csl = nil,
+ type = "literal",
+ },
definition = {
csl = nil,
},
+ degree = {
+ csl = nil,
+ type = "literal",
+ },
+ degreelist = {
+ csl = nil,
+ type = "entrykey",
+ },
department = {
csl = nil,
+ type = "literal",
},
description = {
csl = nil,
@@ -583,11 +1140,22 @@ return {
csl = nil,
},
dimensions = {
- csl = nil,
+ csl = "dimensions",
+ },
+ director = {
+ csl = "director",
+ type = "name",
},
dissyear = {
csl = nil,
},
+ distinctURL = {
+ csl = nil,
+ type = "literal",
+ },
+ division = {
+ csl = "division",
+ },
doi = {
csl = "DOI",
type = "verbatim",
@@ -598,10 +1166,22 @@ return {
dummy = {
csl = nil,
},
+ duration = {
+ csl = nil,
+ type = "literal",
+ },
+ durationtype = {
+ csl = nil,
+ type = "literal",
+ },
edition = {
csl = "edition",
type = "literal",
},
+ editioncredits = {
+ csl = nil,
+ type = "literal",
+ },
editor = {
csl = "editor",
type = "name",
@@ -610,6 +1190,14 @@ return {
csl = nil,
type = "name",
},
+ editoraaddon = {
+ csl = nil,
+ type = "literal",
+ },
+ editoraddon = {
+ csl = nil,
+ type = "name",
+ },
editoratype = {
csl = nil,
type = "key",
@@ -630,6 +1218,9 @@ return {
csl = nil,
type = "key",
},
+ ["editorial-director"] = {
+ csl = "editorial-director",
+ },
editortype = {
csl = nil,
type = "key",
@@ -641,6 +1232,10 @@ return {
email = {
csl = nil,
},
+ endbookyear = {
+ csl = nil,
+ type = "literal",
+ },
endnumber = {
csl = nil,
},
@@ -650,6 +1245,10 @@ return {
endyear = {
csl = nil,
},
+ englishabstract = {
+ csl = nil,
+ type = "literal",
+ },
englishtitle = {
csl = nil,
},
@@ -672,6 +1271,18 @@ return {
csl = nil,
type = "literal",
},
+ eprintdate = {
+ csl = nil,
+ type = "date",
+ },
+ eprintday = {
+ csl = nil,
+ type = "datepart",
+ },
+ eprintmonth = {
+ csl = nil,
+ type = "datepart",
+ },
eprints = {
csl = nil,
},
@@ -679,6 +1290,10 @@ return {
csl = "archive",
type = "literal",
},
+ eprintyear = {
+ csl = nil,
+ type = "datepart",
+ },
erratumeid = {
csl = nil,
},
@@ -697,10 +1312,34 @@ return {
erratumyear = {
csl = nil,
},
+ event = {
+ csl = "event-title",
+ },
+ ["event-date"] = {
+ csl = "event-date",
+ },
+ ["event-place"] = {
+ csl = "event-place",
+ },
+ ["event-title"] = {
+ csl = "event-title",
+ },
eventdate = {
csl = "event-date",
type = "date",
},
+ eventdatelanguage = {
+ csl = nil,
+ type = "verbatim",
+ },
+ eventnumber = {
+ csl = nil,
+ type = "integer",
+ },
+ eventsubtitle = {
+ csl = nil,
+ type = "literal",
+ },
eventtitle = {
csl = "event-title",
type = "literal",
@@ -709,13 +1348,64 @@ return {
csl = nil,
type = "literal",
},
+ eventtype = {
+ csl = nil,
+ type = "literal",
+ },
eventyear = {
csl = nil,
},
+ execdirector = {
+ csl = "director",
+ type = "name",
+ },
+ execproducer = {
+ csl = "executive-producer",
+ type = "name",
+ },
execute = {
csl = nil,
type = "code",
},
+ ["executive-producer"] = {
+ csl = "executive-producer",
+ },
+ exhibfirstdate = {
+ csl = "event-date",
+ type = "date",
+ },
+ exhibfirstlocation = {
+ csl = "event-place",
+ type = "literal",
+ },
+ exhibfirstmuseum = {
+ csl = nil,
+ type = "literal",
+ },
+ exhibseconddate = {
+ csl = nil,
+ type = "date",
+ },
+ exhibsecondlocation = {
+ csl = nil,
+ type = "literal",
+ },
+ exhibsecondmuseum = {
+ csl = nil,
+ type = "literal",
+ },
+ exhibthirddate = {
+ csl = nil,
+ type = "date",
+ },
+ exhibthirdlocation = {
+ csl = nil,
+ type = "literal",
+ },
+ exhibthirdmuseum = {
+ csl = nil,
+ type = "literal",
+ },
faddress = {
csl = nil,
},
@@ -725,6 +1415,14 @@ return {
fax = {
csl = nil,
},
+ festschrift = {
+ csl = nil,
+ type = "name",
+ },
+ festschriftaddon = {
+ csl = nil,
+ type = "literal",
+ },
file = {
csl = nil,
type = "verbatim",
@@ -751,9 +1449,21 @@ return {
fpublisher = {
csl = nil,
},
+ frenchabstract = {
+ csl = nil,
+ type = "literal",
+ },
ftitle = {
csl = nil,
},
+ fulleventdate = {
+ csl = nil,
+ type = "date",
+ },
+ funder = {
+ csl = nil,
+ type = "literal",
+ },
furtherresp = {
csl = nil,
},
@@ -764,14 +1474,37 @@ return {
csl = nil,
type = "literal",
},
+ genre = {
+ csl = "genre",
+ },
germanpages = {
csl = nil,
},
+ gpa = {
+ csl = nil,
+ type = "literal",
+ },
group = {
csl = nil,
},
+ guest = {
+ csl = "guest",
+ },
+ hal_id = {
+ csl = nil,
+ type = "verbatim",
+ },
+ hal_version = {
+ csl = "version",
+ type = "verbatim",
+ },
+ handle = {
+ csl = nil,
+ type = "literal",
+ },
heading = {
csl = nil,
+ type = "literal",
},
hereafter = {
csl = nil,
@@ -780,6 +1513,13 @@ return {
csl = nil,
type = "name",
},
+ honors = {
+ csl = nil,
+ type = "literal",
+ },
+ host = {
+ csl = "host",
+ },
howcited = {
csl = nil,
},
@@ -802,7 +1542,23 @@ return {
csl = nil,
},
illustrator = {
+ csl = "illustrator",
+ },
+ impactfactor = {
csl = nil,
+ type = "literal",
+ },
+ inafterword = {
+ csl = nil,
+ type = "name",
+ },
+ inannotator = {
+ csl = nil,
+ type = "name",
+ },
+ incommentator = {
+ csl = nil,
+ type = "name",
},
indexsorttitle = {
csl = nil,
@@ -812,10 +1568,37 @@ return {
csl = nil,
type = "literal",
},
+ ineditor = {
+ csl = nil,
+ type = "name",
+ },
+ inforeword = {
+ csl = nil,
+ type = "name",
+ },
+ inintroduction = {
+ csl = nil,
+ type = "name",
+ },
institution = {
csl = "publisher",
type = "literal",
},
+ interviewee = {
+ csl = nil,
+ type = "name",
+ },
+ interviewer = {
+ csl = "interviewer",
+ },
+ intranslator = {
+ csl = nil,
+ type = "name",
+ },
+ introducedin = {
+ csl = nil,
+ type = "literal",
+ },
introduction = {
csl = nil,
type = "name",
@@ -824,10 +1607,11 @@ return {
csl = nil,
},
inventor = {
- csl = nil,
+ csl = "author",
},
ipc = {
csl = nil,
+ type = "literal",
},
isan = {
csl = nil,
@@ -862,6 +1646,13 @@ return {
csl = "issue",
type = "literal",
},
+ issued = {
+ csl = "issued",
+ },
+ issues = {
+ csl = nil,
+ type = "literal",
+ },
issuesubtitle = {
csl = nil,
type = "literal",
@@ -881,10 +1672,21 @@ return {
jfmnumber = {
csl = nil,
},
+ jointauthor = {
+ csl = nil,
+ type = "name",
+ },
+ jointauthortype = {
+ csl = nil,
+ type = "literal",
+ },
journal = {
csl = "container-title",
type = "literal",
},
+ journalabbreviation = {
+ csl = "container-title-short",
+ },
journalsubtitle = {
csl = nil,
type = "literal",
@@ -900,16 +1702,30 @@ return {
csl = nil,
type = "literal",
},
+ journalyear = {
+ csl = nil,
+ type = "literal",
+ },
+ jstor = {
+ csl = nil,
+ type = "verbatim",
+ },
juraauthor = {
csl = nil,
},
juratitle = {
csl = nil,
},
+ jurisdiction = {
+ csl = "jurisdiction",
+ },
key = {
csl = nil,
type = "literal",
},
+ keyword = {
+ csl = "keyword",
+ },
keywords = {
csl = nil,
type = "keyword",
@@ -935,10 +1751,15 @@ return {
},
lastaccessed = {
csl = "accessed",
+ type = "literal",
},
lastchecked = {
csl = "accessed",
},
+ layer = {
+ csl = nil,
+ type = "literal",
+ },
lccn = {
csl = nil,
},
@@ -946,6 +1767,14 @@ return {
csl = nil,
type = "literal",
},
+ librarylocation = {
+ csl = "archive-place",
+ type = "literal",
+ },
+ license = {
+ csl = "license",
+ type = "literal",
+ },
lista = {
csl = nil,
type = "literal",
@@ -977,6 +1806,14 @@ return {
csl = "publisher-place",
type = "literal",
},
+ locationa = {
+ csl = nil,
+ type = "literal",
+ },
+ locationb = {
+ csl = nil,
+ type = "literal",
+ },
madadurltest = {
csl = nil,
},
@@ -986,10 +1823,22 @@ return {
main1 = {
csl = nil,
},
+ mainauthor = {
+ csl = nil,
+ type = "name",
+ },
+ maineditor = {
+ csl = nil,
+ type = "name",
+ },
mainsubtitle = {
csl = nil,
type = "literal",
},
+ maintainer = {
+ csl = nil,
+ type = "name",
+ },
maintitle = {
csl = nil,
type = "literal",
@@ -998,6 +1847,18 @@ return {
csl = nil,
type = "literal",
},
+ maintitlenote = {
+ csl = nil,
+ type = "literal",
+ },
+ maintranslator = {
+ csl = nil,
+ type = "name",
+ },
+ major = {
+ csl = nil,
+ type = "literal",
+ },
majorcode = {
csl = nil,
},
@@ -1015,6 +1876,7 @@ return {
},
media = {
csl = "medium",
+ type = "key",
},
medium = {
csl = "medium",
@@ -1022,6 +1884,10 @@ return {
meeting = {
csl = nil,
},
+ minor = {
+ csl = nil,
+ type = "literal",
+ },
misctitle = {
csl = nil,
},
@@ -1044,8 +1910,17 @@ return {
mrnumber = {
csl = nil,
},
+ multieventdate = {
+ csl = nil,
+ type = "literal",
+ },
+ multivenue = {
+ csl = nil,
+ type = "literal",
+ },
name = {
csl = nil,
+ type = "name",
},
namea = {
csl = nil,
@@ -1075,6 +1950,10 @@ return {
csl = nil,
type = "key",
},
+ narrator = {
+ csl = "narrator",
+ type = "name",
+ },
nationality = {
csl = nil,
},
@@ -1084,8 +1963,13 @@ return {
newpage = {
csl = nil,
},
+ newsdate = {
+ csl = nil,
+ type = "date",
+ },
newspaper = {
csl = "container-title",
+ type = "literal",
},
nickname = {
csl = nil,
@@ -1119,18 +2003,57 @@ return {
csl = "number",
type = "literal",
},
+ ["number-of-pages"] = {
+ csl = "number-of-pages",
+ },
+ ["number-of-volumes"] = {
+ csl = "number-of-volumes",
+ },
+ numbera = {
+ csl = nil,
+ type = "literal",
+ },
+ numberb = {
+ csl = nil,
+ type = "literal",
+ },
+ numcites = {
+ csl = nil,
+ type = "integer",
+ },
+ numlectures = {
+ csl = nil,
+ type = "integer",
+ },
numpages = {
+ csl = "number-of-pages",
+ type = "literal",
+ },
+ numstudents = {
csl = nil,
+ type = "integer",
},
oaddress = {
+ csl = "original-publisher-place",
+ },
+ officialpages = {
csl = nil,
+ type = "literal",
+ },
+ officialvolume = {
+ csl = nil,
+ type = "literal",
+ },
+ openaccess = {
+ csl = nil,
+ type = "literal",
},
options = {
csl = nil,
type = "option",
},
opublisher = {
- csl = nil,
+ csl = "original-publisher",
},
["org-short"] = {
csl = nil,
@@ -1139,10 +2062,40 @@ return {
csl = "publisher",
type = "literal",
},
+ organizer = {
+ csl = "organizer",
+ },
origdate = {
csl = "original-date",
type = "date",
},
+ ["origdate+an"] = {
+ csl = nil,
+ type = "literal",
+ },
+ origdatetype = {
+ csl = nil,
+ type = "literal",
+ },
+ origin = {
+ csl = nil,
+ type = "literal",
+ },
+ ["original-author"] = {
+ csl = "original-author",
+ },
+ ["original-date"] = {
+ csl = "original-date",
+ },
+ ["original-publisher"] = {
+ csl = "original-publisher",
+ },
+ ["original-publisher-place"] = {
+ csl = "original-publisher-place",
+ },
+ ["original-title"] = {
+ csl = "original-title",
+ },
originaladdress = {
csl = "original-publisher-place",
},
@@ -1185,13 +2138,24 @@ return {
csl = "original-publisher",
type = "literal",
},
+ origrealdate = {
+ csl = nil,
+ type = "date",
+ },
origtitle = {
csl = "original-title",
type = "literal",
},
+ owner = {
+ csl = nil,
+ type = "name",
+ },
oyear = {
csl = nil,
},
+ page = {
+ csl = "page",
+ },
pagename = {
csl = nil,
},
@@ -1210,10 +2174,41 @@ return {
paper = {
csl = nil,
},
+ pardate = {
+ csl = nil,
+ type = "date",
+ },
+ parpages = {
+ csl = nil,
+ type = "range",
+ },
+ parreporter = {
+ csl = nil,
+ type = "literal",
+ },
+ parseries = {
+ csl = nil,
+ type = "literal",
+ },
part = {
csl = "part",
type = "literal",
},
+ ["part-title"] = {
+ csl = "part-title",
+ },
+ parts = {
+ csl = nil,
+ type = "literal",
+ },
+ parttitle = {
+ csl = nil,
+ type = "literal",
+ },
+ parvolume = {
+ csl = nil,
+ type = "integer",
+ },
patentid = {
csl = nil,
},
@@ -1221,6 +2216,13 @@ return {
csl = nil,
type = "verbatim",
},
+ peerreview = {
+ csl = nil,
+ type = "literal",
+ },
+ performer = {
+ csl = "performer",
+ },
phone = {
csl = nil,
},
@@ -1230,18 +2232,36 @@ return {
pii = {
csl = nil,
},
+ pmc = {
+ csl = nil,
+ type = "literal",
+ },
pmcid = {
csl = "PMCID",
+ type = "literal",
+ },
+ pmid = {
+ csl = "PMID",
+ type = "literal",
},
prebibitem = {
csl = nil,
},
preface = {
csl = nil,
+ type = "name",
},
preprint = {
csl = nil,
},
+ presentationtype = {
+ csl = nil,
+ type = "literal",
+ },
+ presenter = {
+ csl = nil,
+ type = "name",
+ },
presort = {
csl = nil,
type = "literal",
@@ -1256,35 +2276,92 @@ return {
printing = {
csl = nil,
},
+ ["printing-number"] = {
+ csl = "printing-number",
+ },
prioritycountry = {
csl = nil,
+ type = "literal",
},
prioritydate = {
csl = nil,
+ type = "date",
},
prioritynumber = {
csl = nil,
+ type = "literal",
+ },
+ producer = {
+ csl = "producer",
+ type = "name",
+ },
+ production = {
+ csl = nil,
+ type = "literal",
+ },
+ protocollessurl = {
+ csl = nil,
+ type = "uri",
},
pseudonym = {
csl = nil,
},
publication = {
csl = nil,
+ type = "literal",
},
publicationdate = {
csl = nil,
+ type = "date",
+ },
+ publicprice = {
+ csl = nil,
+ type = "literal",
},
publisher = {
csl = "publisher",
type = "literal",
},
+ ["publisher-place"] = {
+ csl = "publisher-place",
+ },
+ publishera = {
+ csl = nil,
+ type = "literal",
+ },
+ publisherb = {
+ csl = nil,
+ type = "literal",
+ },
pubmed = {
csl = "PMID",
},
pubstate = {
- csl = nil,
+ csl = "status",
type = "key",
},
+ rawauthor = {
+ csl = nil,
+ type = "literal",
+ },
+ realauthor = {
+ csl = nil,
+ type = "name",
+ },
+ realdate = {
+ csl = nil,
+ type = "date",
+ },
+ realeditor = {
+ csl = nil,
+ type = "name",
+ },
+ recipient = {
+ csl = "recipient",
+ },
+ references = {
+ csl = "references",
+ },
related = {
csl = nil,
type = "entrykey",
@@ -1301,6 +2378,14 @@ return {
csl = nil,
type = "literal",
},
+ reportnumber = {
+ csl = nil,
+ type = "literal",
+ },
+ repository = {
+ csl = nil,
+ type = "uri",
+ },
["reprinted-from"] = {
csl = nil,
},
@@ -1316,33 +2401,129 @@ return {
},
requestnumber = {
csl = nil,
+ type = "literal",
},
responsible = {
csl = nil,
},
+ revdauthor = {
+ csl = nil,
+ type = "name",
+ },
+ revdeditor = {
+ csl = nil,
+ type = "name",
+ },
+ revdshorttitle = {
+ csl = nil,
+ type = "literal",
+ },
+ revdsubtitle = {
+ csl = nil,
+ type = "literal",
+ },
+ revdtitle = {
+ csl = nil,
+ type = "literal",
+ },
+ revdtitleaddon = {
+ csl = nil,
+ type = "literal",
+ },
review = {
csl = nil,
},
+ ["reviewed-author"] = {
+ csl = "reviewed-author",
+ },
+ ["reviewed-genre"] = {
+ csl = "reviewed-genre",
+ },
+ ["reviewed-title"] = {
+ csl = "reviewed-title",
+ },
+ reviewer = {
+ csl = nil,
+ type = "name",
+ },
+ reviewerinstitution = {
+ csl = nil,
+ type = "literal",
+ },
revision = {
csl = nil,
},
+ role = {
+ csl = nil,
+ type = "key",
+ },
+ scale = {
+ csl = "scale",
+ },
school = {
csl = "publisher",
type = "literal",
},
+ science = {
+ csl = nil,
+ type = "literal",
+ },
score = {
csl = nil,
},
+ scribe = {
+ csl = nil,
+ type = "name",
+ },
+ script = {
+ csl = nil,
+ type = "literal",
+ },
+ ["script-writer"] = {
+ csl = "script-writer",
+ },
+ scriptwriter = {
+ csl = nil,
+ type = "name",
+ },
section = {
+ csl = "section",
+ type = "literal",
+ },
+ semesters = {
csl = nil,
+ type = "entrykey",
},
series = {
csl = "collection-title",
type = "literal",
},
+ ["series-creator"] = {
+ csl = "series-creator",
+ },
+ seriesaddon = {
+ csl = nil,
+ type = "literal",
+ },
seriesedition = {
csl = nil,
},
+ serieseditor = {
+ csl = nil,
+ type = "name",
+ },
+ seriesseries = {
+ csl = nil,
+ type = "literal",
+ },
+ setlabel = {
+ csl = nil,
+ type = "literal",
+ },
+ shelfmark = {
+ csl = nil,
+ type = "literal",
+ },
short = {
csl = nil,
},
@@ -1353,10 +2534,30 @@ return {
csl = nil,
type = "name",
},
+ shortblog = {
+ csl = nil,
+ type = "literal",
+ },
+ shortbookauthor = {
+ csl = nil,
+ type = "name",
+ },
+ shortbooktitle = {
+ csl = nil,
+ type = "literal",
+ },
+ shortcollection = {
+ csl = nil,
+ type = "literal",
+ },
shorteditor = {
csl = nil,
type = "name",
},
+ shortform = {
+ csl = nil,
+ type = "literal",
+ },
shorthand = {
csl = nil,
type = "literal",
@@ -1365,10 +2566,34 @@ return {
csl = nil,
type = "literal",
},
+ shortissuetitle = {
+ csl = nil,
+ type = "literal",
+ },
shortjournal = {
csl = "container-title-short",
type = "literal",
},
+ shortlabeltitle = {
+ csl = nil,
+ type = "literal",
+ },
+ shortlibrary = {
+ csl = nil,
+ type = "literal",
+ },
+ shortmaintitle = {
+ csl = nil,
+ type = "literal",
+ },
+ shortorganization = {
+ csl = nil,
+ type = "literal",
+ },
+ shortserie = {
+ csl = nil,
+ type = "literal",
+ },
shortseries = {
csl = nil,
type = "literal",
@@ -1376,10 +2601,22 @@ return {
shortsubarchive = {
csl = nil,
},
+ shortsubseries = {
+ csl = nil,
+ type = "literal",
+ },
+ shortsubsubseries = {
+ csl = nil,
+ type = "literal",
+ },
shorttitle = {
csl = "title-short",
type = "literal",
},
+ shorttranslator = {
+ csl = nil,
+ type = "name",
+ },
shorturl = {
csl = nil,
},
@@ -1395,12 +2632,20 @@ return {
sig4 = {
csl = nil,
},
+ sign = {
+ csl = nil,
+ type = "literal",
+ },
size = {
csl = nil,
},
slaccitation = {
csl = nil,
},
+ smfprice = {
+ csl = nil,
+ type = "literal",
+ },
["sort-short"] = {
csl = nil,
},
@@ -1418,6 +2663,10 @@ return {
csl = nil,
type = "name",
},
+ sortshelfmark = {
+ csl = nil,
+ type = "literal",
+ },
sortshorthand = {
csl = nil,
type = "literal",
@@ -1426,6 +2675,10 @@ return {
csl = nil,
type = "literal",
},
+ sortvolume = {
+ csl = nil,
+ type = "literal",
+ },
sortyear = {
csl = nil,
type = "integer",
@@ -1433,8 +2686,17 @@ return {
source1 = {
csl = nil,
},
+ specdata = {
+ csl = nil,
+ type = "literal",
+ },
+ speciality = {
+ csl = nil,
+ type = "literal",
+ },
specialitycode = {
csl = nil,
+ type = "literal",
},
ssedition = {
csl = nil,
@@ -1455,10 +2717,11 @@ return {
csl = nil,
},
status = {
- csl = nil,
+ csl = "status",
+ type = "key",
},
stdcode = {
- csl = nil,
+ csl = "number",
},
stitle = {
csl = nil,
@@ -1469,16 +2732,86 @@ return {
subarchive = {
csl = nil,
},
+ submitted = {
+ csl = "submitted",
+ },
+ subnumber = {
+ csl = nil,
+ type = "integer",
+ },
+ subseries = {
+ csl = nil,
+ type = "literal",
+ },
+ subsubnumber = {
+ csl = nil,
+ type = "literal",
+ },
+ subsubseries = {
+ csl = nil,
+ type = "literal",
+ },
subtitle = {
csl = nil,
type = "literal",
},
+ summarytitle = {
+ csl = nil,
+ type = "literal",
+ },
+ supervisor = {
+ csl = nil,
+ type = "name",
+ },
+ supplement = {
+ csl = "supplement",
+ },
+ supplemental = {
+ csl = nil,
+ type = "literal",
+ },
+ supplementala = {
+ csl = nil,
+ type = "literal",
+ },
+ supplementalb = {
+ csl = nil,
+ type = "literal",
+ },
+ support = {
+ csl = nil,
+ type = "literal",
+ },
+ swhid = {
+ csl = nil,
+ type = "verbatim",
+ },
+ swhidcore = {
+ csl = nil,
+ type = "verbatim",
+ },
symposium = {
csl = nil,
},
+ systemreq = {
+ csl = nil,
+ type = "literal",
+ },
text = {
csl = nil,
},
+ thesisdate = {
+ csl = nil,
+ type = "date",
+ },
+ thesistype = {
+ csl = nil,
+ type = "verbatim",
+ },
+ thumbnail = {
+ csl = nil,
+ type = "verbatim",
+ },
timestamp = {
csl = nil,
},
@@ -1486,12 +2819,36 @@ return {
csl = "title",
type = "literal",
},
+ ["title-short"] = {
+ csl = "title-short",
+ },
+ titlea = {
+ csl = nil,
+ type = "literal",
+ },
titleaddon = {
csl = nil,
type = "literal",
},
+ titleb = {
+ csl = nil,
+ type = "literal",
+ },
+ titleisdescription = {
+ csl = nil,
+ type = "literal",
+ },
+ titlekey = {
+ csl = nil,
+ type = "key",
+ },
titlenote = {
csl = nil,
+ type = "literal",
+ },
+ titletype = {
+ csl = nil,
+ type = "literal",
},
totalpages = {
csl = nil,
@@ -1509,6 +2866,26 @@ return {
csl = "translator",
type = "name",
},
+ translatora = {
+ csl = nil,
+ type = "name",
+ },
+ translatoratype = {
+ csl = nil,
+ type = "key",
+ },
+ translatorb = {
+ csl = nil,
+ type = "name",
+ },
+ translatorbtype = {
+ csl = nil,
+ type = "key",
+ },
+ translatortype = {
+ csl = nil,
+ type = "key",
+ },
transnumber = {
csl = nil,
},
@@ -1528,12 +2905,20 @@ return {
csl = "genre",
type = "key",
},
+ typeaddon = {
+ csl = nil,
+ type = "literal",
+ },
typeoflit = {
csl = nil,
},
umfnumber = {
csl = nil,
},
+ update = {
+ csl = nil,
+ type = "date",
+ },
updated = {
csl = nil,
},
@@ -1548,17 +2933,26 @@ return {
csl = "accessed",
type = "date",
},
+ urldescription = {
+ csl = nil,
+ type = "literal",
+ },
urlnewline = {
csl = nil,
},
+ urls = {
+ csl = nil,
+ type = "uri",
+ },
urltype = {
csl = nil,
},
urlyear = {
- csl = nil,
+ csl = "accessed",
},
urn = {
csl = nil,
+ type = "verbatim",
},
usera = {
csl = nil,
@@ -1584,6 +2978,10 @@ return {
csl = nil,
type = "literal",
},
+ usualtitle = {
+ csl = nil,
+ type = "literal",
+ },
value = {
csl = nil,
},
@@ -1607,26 +3005,124 @@ return {
csl = "version",
type = "literal",
},
+ versiona = {
+ csl = nil,
+ type = "literal",
+ },
+ versionb = {
+ csl = nil,
+ type = "literal",
+ },
versiontype = {
csl = nil,
},
volformat = {
csl = nil,
},
+ volsorder = {
+ csl = nil,
+ type = "literal",
+ },
volume = {
csl = "volume",
type = "integer",
},
+ ["volume-title"] = {
+ csl = "volume-title",
+ },
+ ["volume-title-short"] = {
+ csl = "volume-title-short",
+ },
volumes = {
csl = "number-of-volumes",
type = "integer",
},
volumetitle = {
+ csl = "volume-title",
+ },
+ with = {
+ csl = nil,
+ type = "name",
+ },
+ withauthor = {
+ csl = nil,
+ type = "name",
+ },
+ withauthortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withbookauthor = {
+ csl = nil,
+ type = "name",
+ },
+ withbookauthortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withbookeditor = {
+ csl = nil,
+ type = "name",
+ },
+ withbookeditortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withbooktranslator = {
+ csl = nil,
+ type = "name",
+ },
+ withbooktranslatortype = {
+ csl = nil,
+ type = "literal",
+ },
+ witheditor = {
+ csl = nil,
+ type = "name",
+ },
+ witheditortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withmainauthor = {
csl = nil,
+ type = "name",
+ },
+ withmainauthortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withmaineditor = {
+ csl = nil,
+ type = "name",
+ },
+ withmaineditortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withmaintranslator = {
+ csl = nil,
+ type = "name",
+ },
+ withmaintranslatortype = {
+ csl = nil,
+ type = "literal",
+ },
+ withtranslator = {
+ csl = nil,
+ type = "name",
+ },
+ withtranslatortype = {
+ csl = nil,
+ type = "literal",
},
word = {
csl = nil,
},
+ writer = {
+ csl = nil,
+ type = "name",
+ },
xdata = {
csl = nil,
type = "entrykey",
@@ -1654,6 +3150,10 @@ return {
zblnumber = {
csl = nil,
},
+ zenon = {
+ csl = nil,
+ type = "verbatim",
+ },
zip = {
csl = nil,
},
diff --git a/biblio/citation-style-language/citeproc-cli.lua b/biblio/citation-style-language/citeproc-cli.lua
index 9023728473..4938267d3a 100644
--- a/biblio/citation-style-language/citeproc-cli.lua
+++ b/biblio/citation-style-language/citeproc-cli.lua
@@ -143,6 +143,12 @@ local function process_aux_file(aux_file)
end
local style_class = engine:get_style_class()
+ if style_class == "in-text" then
+ for _, citation in ipairs(citations) do
+ citation.properties.noteIndex = 0
+ end
+ end
+
local citation_strings = core.process_citations(engine, citations)
-- util.debug(citation_strings)
@@ -153,7 +159,7 @@ local function process_aux_file(aux_file)
local citation_id = citation.citationID
if citation_id ~= "@nocite" then
local citation_str = citation_strings[citation_id]
- output_string = output_string .. string.format("\\cslcite{%s}{{%s}{%s}}\n", citation_id, style_class, citation_str)
+ output_string = output_string .. string.format("\\cslcite{%s}{%s}\n", citation_id, citation_str)
end
end
diff --git a/biblio/citation-style-language/citeproc-context.lua b/biblio/citation-style-language/citeproc-context.lua
index 08be158bcf..f0c42e64fc 100644
--- a/biblio/citation-style-language/citeproc-context.lua
+++ b/biblio/citation-style-language/citeproc-context.lua
@@ -265,7 +265,7 @@ function Context:get_macro(name)
end
function Context:get_simple_term(name, form, plural)
- assert(self.locale)
+ -- assert(self.locale)
return self.locale:get_simple_term(name, form, plural)
end
diff --git a/biblio/citation-style-language/citeproc-element.lua b/biblio/citation-style-language/citeproc-element.lua
index 197616cb08..4ef2b7c420 100644
--- a/biblio/citation-style-language/citeproc-element.lua
+++ b/biblio/citation-style-language/citeproc-element.lua
@@ -95,6 +95,34 @@ function Element:process_children_nodes(node)
end
+function Element.make_name_inheritance(name, node)
+ name:set_attribute(node, "and")
+ name:set_attribute(node, "delimiter-precedes-et-al")
+ name:set_attribute(node, "delimiter-precedes-last")
+ name:set_number_attribute(node, "et-al-min")
+ name:set_number_attribute(node, "et-al-use-first")
+ name:set_number_attribute(node, "et-al-subsequent-min")
+ name:set_number_attribute(node, "et-al-subsequent-use-first")
+ name:set_bool_attribute(node, "et-al-use-last")
+ name:set_bool_attribute(node, "initialize")
+ name:set_attribute(node, "initialize-with")
+ name:set_attribute(node, "name-as-sort-order")
+ name:set_attribute(node, "sort-separator")
+ local delimiter = node:get_attribute("name-delimiter")
+ if delimiter then
+ name.delimiter = delimiter
+ end
+ local form = node:get_attribute("name-form")
+ if form then
+ name.form = form
+ end
+ local names_delimiter = node:get_attribute("names-delimiter")
+ if names_delimiter then
+ name.names_delimiter = names_delimiter
+ end
+end
+
+
function Element:build_ir(engine, state, context)
return self:build_children_ir(engine, state, context)
end
@@ -430,7 +458,8 @@ end
function Element:format_ordinal_number_parts(number_parts, form, gender, context)
for i = 1, 2 do
local part = number_parts[i]
- if string.match(part, "%d+") then
+ -- Values like "2nd" are kept the in the original form.
+ if string.match(part, "^%d+$") then
local number = tonumber(part)
if form == "long-ordinal" and number >= 1 and number <= 10 then
number_parts[i] = context:get_simple_term(string.format("long-ordinal-%02d", number))
diff --git a/biblio/citation-style-language/citeproc-engine.lua b/biblio/citation-style-language/citeproc-engine.lua
index d59c3bb4ea..e8a77450eb 100644
--- a/biblio/citation-style-language/citeproc-engine.lua
+++ b/biblio/citation-style-language/citeproc-engine.lua
@@ -9,21 +9,16 @@ local engine = {}
local dom = require("luaxml-domobject")
local nodes = require("citeproc-nodes")
+local Element = require("citeproc-element").Element
local Style = require("citeproc-node-style").Style
local Locale = require("citeproc-node-locale").Locale
local Context = require("citeproc-context").Context
local IrState = require("citeproc-context").IrState
-local Rendered = require("citeproc-ir-node").Rendered
-local YearSuffix = require("citeproc-ir-node").YearSuffix
+local InlineElement = require("citeproc-output").InlineElement
-- local OutputFormat = require("citeproc-output").OutputFormat
local LatexWriter = require("citeproc-output").LatexWriter
local HtmlWriter = require("citeproc-output").HtmlWriter
local SortStringFormat = require("citeproc-output").SortStringFormat
-local DisamStringFormat = require("citeproc-output").DisamStringFormat
-local InlineElement = require("citeproc-output").InlineElement
-local Micro = require("citeproc-output").Micro
-local Formatted = require("citeproc-output").Formatted
-local PlainText = require("citeproc-output").PlainText
local util = require("citeproc-util")
@@ -56,8 +51,7 @@ function CiteProc.new(sys, style, lang, force_lang)
o.opt = {
-- Similar to citeproc-js's development_extensions.wrap_url_and_doi
- url_link = false,
- doi_link = false, -- also applied to PMID and PMCID
+ wrap_url_and_doi = false,
title_link = false,
}
@@ -173,16 +167,10 @@ function CiteProc:updateUncitedItems(uncited_ids)
self.note_citations_map = {}
end
+
function CiteProc:processCitationCluster(citation, citationsPre, citationsPost)
-- util.debug(citation)
- -- util.debug(citationsPre)
- -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt
- if not citation.properties then
- citation.properties = {}
- end
- if not citation.properties.noteIndex then
- citation.properties.noteIndex = 0
- end
+ citation = self:normalize_citation_input(citation)
-- Registor citation
self.registry.citations_by_id[citation.citationID] = citation
@@ -228,6 +216,7 @@ function CiteProc:processCitationCluster(citation, citationsPre, citationsPost)
local output = {}
local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs)
+ tainted_citation_ids[citation.citationID] = true
-- util.debug(tainted_citation_ids)
-- params.bibchange = #tainted_citation_ids > 0
@@ -235,19 +224,37 @@ function CiteProc:processCitationCluster(citation, citationsPre, citationsPost)
local citation_ = self.registry.citations_by_id[citation_id]
local citation_index = citation_.citation_index
- local citation_str = self:build_citation_str(citation_)
+
+ local mode = citation_.properties.mode
+ if mode == "suppress-author" and self.style.class == "note" then
+ mode = nil
+ end
+ local citation_elemement = self.style.citation
+ if mode == "author-only" and self.style.intext then
+ citation_elemement = self.style.intext
+ end
+
+ local citation_str = citation_elemement:build_citation_str(citation_, self)
table.insert(output, {citation_index, citation_str, citation_id})
end
- -- util.debug(output)
return {params, output}
end
--- A variant of processCitationCluster() for easy use with LaTeX.
--- It should be run after refreshing the registry (updateItems()) with all items
-function CiteProc:process_citation(citation)
- -- util.debug(citation)
- -- util.debug(citationsPre)
+function CiteProc:normalize_citation_input(citation)
+ citation = util.deep_copy(citation)
+
+ if not citation.citationID then
+ citation.citationID = "CITATION-" .. tostring(#self.registry.citation_list)
+ end
+
+ if not citation.citationItems then
+ citation.citationItems = {}
+ end
+ for i, cite_item in ipairs(citation.citationItems) do
+ citation.citationItems[i] = self:normalize_cite_item(cite_item)
+ end
+
-- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt
if not citation.properties then
citation.properties = {}
@@ -256,6 +263,48 @@ function CiteProc:process_citation(citation)
citation.properties.noteIndex = 0
end
+ return citation
+end
+
+function CiteProc:normalize_cite_item(cite_item)
+ -- Shallow copy
+ cite_item = util.clone(cite_item)
+ cite_item.id = tostring(cite_item.id)
+
+ -- Use "page" as locator label if missing
+ -- label_PluralWithAmpersand.txt
+ if cite_item.locator and not cite_item.label then
+ cite_item.label = "page"
+ end
+
+ if cite_item.prefix then
+ -- Assert CSL rich-text or HTML-like tagged string
+ if cite_item.prefix == "" then
+ cite_item.prefix = nil
+ else
+ cite_item.prefix = InlineElement:parse(cite_item.prefix)
+ end
+ end
+ if cite_item.suffix then
+ if cite_item.suffix == "" then
+ cite_item.suffix = nil
+ else
+ cite_item.suffix = InlineElement:parse(cite_item.suffix)
+ end
+ end
+
+ return cite_item
+end
+
+-- A variant of processCitationCluster() for easy use with LaTeX.
+-- It should be run after refreshing the registry (updateItems()) with all items
+function CiteProc:process_citation(citation)
+ -- util.debug(citation)
+ -- util.debug(citationsPre)
+ -- Fix missing noteIndex: sort_CitationNumberPrimaryAscendingViaMacroCitation.txt
+
+ citation = self:normalize_citation_input(citation)
+
-- Registor citation
self.registry.citations_by_id[citation.citationID] = citation
@@ -276,12 +325,21 @@ function CiteProc:process_citation(citation)
-- self:updateItems(item_ids)
for i, cite_item in ipairs(citation.citationItems) do
- cite_item.id = tostring(cite_item.id)
self:get_item(cite_item.id)
end
local tainted_citation_ids = self:get_tainted_citaion_ids(citation_note_pairs)
- local citation_str = self:build_citation_str(citation)
+
+ local mode = citation.properties.mode
+ if mode == "suppress-author" and self.style.class == "note" then
+ mode = nil
+ end
+ local citation_elemement = self.style.citation
+ if mode == "author-only" and self.style.intext then
+ citation_elemement = self.style.intext
+ end
+
+ local citation_str = citation_elemement:build_citation_str(citation, self)
return citation_str
end
@@ -310,9 +368,14 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs)
local previous_cite
for _, cite_item in ipairs(citation.citationItems) do
- tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation)
- self.cite_last_note_numbers[cite_item.id] = note_number
- previous_cite = cite_item
+ tainted = self:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation, citation)
+
+ -- 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 (self.style.class == "note" and note_number > 0) or citation.properties.mode ~= "author-only" then
+ self.cite_last_note_numbers[cite_item.id] = note_number
+ previous_cite = cite_item
+ end
end
if tainted then
@@ -323,7 +386,9 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs)
self.note_citations_map[note_number] = {}
end
table.insert(self.note_citations_map[note_number], citation.citationID)
- previous_citation = citation
+ if citation.properties.mode ~= "author-only" then
+ previous_citation = citation
+ end
end
-- Update tainted citation ids because of citation-number's change
@@ -339,9 +404,20 @@ function CiteProc:get_tainted_citaion_ids(citation_note_pairs)
return tainted_citation_ids
end
-function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation)
+function CiteProc:set_cite_item_position(cite_item, note_number, previous_cite, previous_citation, citation)
local position = util.position_map["first"]
+ -- 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 self.style.class == "note" and note_number == 0 then
+ cite_item.position_level = position
+ return false
+ elseif citation.properties.mode == "author-only" then
+ -- discretionary_IbidInAuthorDateStyleWithoutIntext.txt
+ cite_item.position_level = position
+ return false
+ end
+
local first_reference_note_number = self.cite_first_note_numbers[cite_item.id]
if first_reference_note_number then
position = util.position_map["subsequent"]
@@ -429,1163 +505,24 @@ function CiteProc:_get_cite_position(item, preceding_cite)
end
end
-function CiteProc:build_citation_str(citation)
- -- util.debug(citation.citationID)
- local items = {}
- for i, cite_item in ipairs(citation.citationItems) do
- cite_item.id = tostring(cite_item.id)
- -- util.debug(cite_item.id)
-
- -- Use "page" as locator label if missing
- -- label_PluralWithAmpersand.txt
- if cite_item.locator and not cite_item.label then
- cite_item.label = "page"
- end
-
- table.insert(items, cite_item)
- end
-
- if self.registry.requires_sorting then
- self:sort_bibliography()
- end
-
- local citation_str = self:build_cluster(items)
- return citation_str
-end
-
-function CiteProc:build_cluster(citation_items)
- local output_format = self.output_format
- local irs = {}
- citation_items = self:sorted_citation_items(citation_items)
- for _, cite_item in ipairs(citation_items) do
- local ir = self:build_fully_disambiguated_ir(cite_item, output_format)
- table.insert(irs, ir)
- end
-
- if self.style.citation.cite_grouping then
- irs = self:group_cites(irs)
- else
- local citation_collapse = self.style.citation.collapse
- if citation_collapse == "year" or citation_collapse == "year-suffix" or
- citation_collapse == "year-suffix-ranged" then
- irs = self:group_cites(irs)
- end
- end
-
- if self.style.citation.collapse then
- self:collapse_cites(irs)
- end
-
- -- Capitalize first
- for i, ir in ipairs(irs) do
- -- local layout_prefix
- -- local layout_affixes = self.style.citation.layout.affixes
- -- if layout_affixes then
- -- layout_prefix = layout_affixes.prefix
- -- end
- local prefix = citation_items[i].prefix
- if prefix then
- if prefix and string.match(prefix, "[.!?]%s*$") and #util.split(util.strip(prefix)) > 1 then
- ir:capitalize_first_term()
- end
- else
- local delimiter = self.style.citation.layout.delimiter
- if i == 1 or not delimiter or string.match(delimiter, "[.!?]%s*$") then
- ir:capitalize_first_term()
- end
- end
- end
-
- -- util.debug(irs)
-
- local citation_delimiter = self.style.citation.layout.delimiter
- local citation_stream = {}
-
- local context = Context:new()
- context.engine = self
- context.style = self.style
- context.area = self.style.citation
- context.in_bibliography = false
- context.locale = self:get_locale(self.lang)
- context.name_inheritance = self.style.citation.name_inheritance
- context.format = output_format
-
- local previous_ir
- for i, ir in ipairs(irs) do
- local cite_prefix = citation_items[i].prefix
- local cite_suffix = citation_items[i].suffix
- if not ir.collapse_suppressed then
- local ir_inlines = ir:flatten(output_format)
- if #ir_inlines > 0 then
- -- Make sure ir_inlines has outputs contents.
- -- collapse_AuthorCollapseNoDateSorted.txt
- if previous_ir then
- if previous_ir.own_delimiter then
- table.insert(citation_stream, PlainText:new(previous_ir.own_delimiter))
- elseif citation_delimiter and not (cite_prefix and util.startswith(cite_prefix, ",")) then
- table.insert(citation_stream, PlainText:new(citation_delimiter))
- end
- end
-
- if cite_prefix then
- table.insert(citation_stream, Micro:new(InlineElement:parse(cite_prefix, context)))
- end
-
- -- util.debug(ir)
- util.extend(citation_stream, ir_inlines)
- previous_ir = ir
-
- if cite_suffix then
- table.insert(citation_stream, Micro:new(InlineElement:parse(cite_suffix, context)))
- end
- end
- end
- end
- -- util.debug(citation_stream)
-
- if context.area.layout.affixes then
- local affixes = context.area.layout.affixes
- if affixes.prefix then
- table.insert(citation_stream, 1, PlainText:new(affixes.prefix))
- end
- if affixes.suffix then
- table.insert(citation_stream, PlainText:new(affixes.suffix))
- end
- end
- -- util.debug(citation_stream)
-
- if #citation_stream > 0 and context.area.layout.formatting then
- citation_stream = {Formatted:new(citation_stream, context.area.layout.formatting)}
- end
-
- if #citation_stream == 0 then
- citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")}
- end
-
- -- util.debug(citation_stream)
- local str = output_format:output(citation_stream, context)
- str = util.strip(str)
-
- return str
-end
-
-function CiteProc:sorted_citation_items(items)
- local citation_sort = self.style.citation.sort
- if not citation_sort then
- return items
- end
-
- local state = IrState:new()
- local context = Context:new()
- context.engine = self
- context.style = self.style
- context.area = self.style.citation
- context.in_bibliography = false
- context.locale = self:get_locale(self.lang)
- context.name_inheritance = self.style.citation.name_inheritance
- context.format = SortStringFormat:new()
- -- context.id = id
- context.cite = nil
- -- context.reference = self:get_item(id)
-
- items = citation_sort:sort(items, state, context)
- return items
-end
-
-function CiteProc:build_fully_disambiguated_ir(cite_item, output_format)
- local cite_ir = self:build_ambiguous_ir(cite_item, output_format)
- -- util.debug(cite_ir)
- cite_ir = self:disambiguate_add_givenname(cite_ir)
- cite_ir = self:disambiguate_add_names(cite_ir)
- cite_ir = self:disambiguate_conditionals(cite_ir)
- cite_ir = self:disambiguate_add_year_suffix(cite_ir)
- return cite_ir
-end
-
-function CiteProc:build_ambiguous_ir(cite_item, output_format)
- local state = IrState:new(self.style)
- cite_item.id = tostring(cite_item.id)
- local context = Context:new()
- context.engine = self
- context.style = self.style
- context.area = self.style.citation
- context.locale = self:get_locale(self.lang)
- context.name_inheritance = self.style.citation.name_inheritance
- context.format = output_format
- context.id = cite_item.id
- context.cite = cite_item
- -- context.reference = self:get_item(cite_item.id)
- context.reference = self.registry.registry[cite_item.id]
-
- local ir
- if context.reference then
- ir = self.style.citation:build_ir(self, state, context)
- else
- ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self)
- end
-
- ir.cite_item = cite_item
- ir.reference = context.reference
- ir.ir_index = #self.disam_irs + 1
- table.insert(self.disam_irs, ir)
- ir.is_ambiguous = false
- ir.disam_level = 0
-
- -- Formattings like font-style are ignored for disambiguation.
- local disam_format = DisamStringFormat:new()
- local inlines = ir:flatten(disam_format)
- local disam_str = disam_format:output(inlines, context)
- ir.disam_str = disam_str
-
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
-
- for ir_index, ir_ in pairs(self.cite_irs_by_output[disam_str]) do
- if ir_.cite_item.id ~= cite_item.id then
- ir.is_ambiguous = true
- break
- end
- end
- self.cite_irs_by_output[disam_str][ir.ir_index] = ir
-
- return ir
-end
-
-function CiteProc:disambiguate_add_givenname(cite_ir)
- if self.style.citation.disambiguate_add_givenname then
- -- util.debug("disambiguate_add_givenname: " .. cite_ir.cite_item.id)
-
- local gn_disam_rule = self.style.citation.givenname_disambiguation_rule
- if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then
- cite_ir = self:disambiguate_add_givenname_all_names(cite_ir)
- elseif gn_disam_rule == "primary-name" or gn_disam_rule == "primary-name-with-initials" then
- cite_ir = self:disambiguate_add_givenname_primary_name(cite_ir)
- elseif gn_disam_rule == "by-cite" then
- cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir)
- end
- end
- return cite_ir
-end
-
--- TODO: reorganize this code
-function CiteProc:disambiguate_add_givenname_all_names(cite_ir)
- -- 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 = #self.person_names + 1
- table.insert(self.person_names, person_name_ir)
- end
-
- if not self.person_names_by_output[name_output] then
- self.person_names_by_output[name_output] = {}
- end
- self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir
-
- local ambiguous_name_irs = {}
- local ambiguous_same_output_irs = {}
-
- for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do
- if pn_ir.full_name ~= person_name_ir.full_name then
- table.insert(ambiguous_name_irs, pn_ir)
- end
- if pn_ir.name_output == person_name_ir.name_output then
- table.insert(ambiguous_same_output_irs, pn_ir)
- 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
- end
-
- for _, pn_ir in ipairs(ambiguous_same_output_irs) do
- -- expand one name
- if pn_ir.disam_variants_index < #pn_ir.disam_variants then
- pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1
- pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index]
- pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output]
-
- if not self.person_names_by_output[pn_ir.name_output] then
- self.person_names_by_output[pn_ir.name_output] = {}
- end
- self.person_names_by_output[pn_ir.name_output][pn_ir.person_name_index] = pn_ir
- 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(self.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
- table.insert(ambiguous_same_output_irs, pn_ir)
- end
- end
-
- end
- end
-
- -- update cite_ir output
- local disam_format = DisamStringFormat:new()
- local inlines = cite_ir:flatten(disam_format)
- local disam_str = disam_format:output(inlines, context)
- cite_ir.disam_str = disam_str
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
- self.cite_irs_by_output[disam_str][cite_ir.ir_index] = cite_ir
-
- -- update ambiguous_cite_irs and ambiguous_same_output_irs
- local ambiguous_cite_irs = {}
- for ir_index, ir_ in pairs(self.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
- end
- if #ambiguous_cite_irs == 0 then
- cite_ir.is_ambiguous = false
- end
-
- return cite_ir
-end
-
-function CiteProc:disambiguate_add_givenname_primary_name(cite_ir)
- if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then
- return cite_ir
- 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 = #self.person_names + 1
- table.insert(self.person_names, person_name_ir)
- end
- if not self.person_names_by_output[name_output] then
- self.person_names_by_output[name_output] = {}
- end
- self.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir
-
- local ambiguous_name_irs = {}
- local ambiguous_same_output_irs = {}
-
- for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do
- if pn_ir.full_name ~= person_name_ir.full_name then
- table.insert(ambiguous_name_irs, pn_ir)
- end
- if pn_ir.name_output == person_name_ir.name_output then
- table.insert(ambiguous_same_output_irs, pn_ir)
- end
- end
-
- for _, name_variant in ipairs(person_name_ir.disam_variants) do
- if #ambiguous_name_irs == 0 then
- break
- end
-
- for _, pn_ir in ipairs(ambiguous_same_output_irs) do
- -- expand one name
- if pn_ir.disam_variants_index < #pn_ir.disam_variants then
- pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1
- pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index]
- pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output]
-
- if not self.person_names_by_output[pn_ir.name_output] then
- self.person_names_by_output[pn_ir.name_output] = {}
- end
- self.person_names_by_output[pn_ir.name_output][person_name_ir.person_name_index] = person_name_ir
- end
- end
-
- -- update ambiguous_name_irs and ambiguous_same_output_irs
- ambiguous_name_irs = {}
- ambiguous_same_output_irs = {}
- for _, pn_ir in pairs(self.person_names_by_output[person_name_ir.name_output]) do
- if pn_ir.full_name ~= person_name_ir.full_name then
- table.insert(ambiguous_name_irs, pn_ir)
- end
- if pn_ir.name_output == person_name_ir.name_output then
- table.insert(ambiguous_same_output_irs, pn_ir)
- end
- end
- end
-
- return cite_ir
-end
-
-function CiteProc:disambiguate_add_givenname_by_cite(cite_ir)
- if not cite_ir.is_ambiguous then
- return cite_ir
- end
- if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then
- return cite_ir
- end
-
- for _, ir_ in ipairs(self.disam_irs) do
- -- util.debug(ir_.cite_item.id)
- -- util.debug(ir_.disam_str)
- end
-
- local disam_format = DisamStringFormat:new()
-
- local ambiguous_cite_irs = {}
- local ambiguous_same_output_irs = {}
-
- for ir_index, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do
- if ir_.cite_item.id ~= cite_ir.cite_item.id then
- table.insert(ambiguous_cite_irs, ir_)
- end
- if ir_.disam_str == cite_ir.disam_str then
- table.insert(ambiguous_same_output_irs, ir_)
- end
- 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
- 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)
- local disam_str = disam_format:output(inlines, context)
- ir_.disam_str = disam_str
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
- self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
- end
- end
- end
-
- -- update ambiguous_cite_irs and ambiguous_same_output_irs
- ambiguous_cite_irs = {}
- ambiguous_same_output_irs = {}
- for ir_index, ir_ in pairs(self.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
- if ir_.disam_str == cite_ir.disam_str then
- table.insert(ambiguous_same_output_irs, ir_)
- end
- end
-
- -- util.debug(#ambiguous_cite_irs)
-
- if #ambiguous_cite_irs == 0 then
- cite_ir.is_ambiguous = false
- return cite_ir
- end
-
- end
- end
-
- return cite_ir
-end
-
-local function find_first_name_ir(ir)
- if ir._type == "NameIr" then
- return ir
- end
- if ir.children then
- for _, child in ipairs(ir.children) do
- local name_ir = find_first_name_ir(child)
- if name_ir then
- return name_ir
- end
- end
- end
- return nil
-end
-
-function CiteProc:disambiguate_add_names(cite_ir)
- if not self.style.citation.disambiguate_add_names then
- return cite_ir
- end
-
- 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
-
- if not cite_ir.is_ambiguous then
- return cite_ir
- end
-
- if not name_ir or not name_ir.et_al_abbreviation then
- return cite_ir
- end
-
- -- util.debug("disambiguate_add_names: " .. cite_ir.cite_item.id)
-
- 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
- if #cite_ir.name_ir.hidden_name_irs == 0 then
- break
- end
-
- local ambiguous_cite_irs = {}
- local ambiguous_same_output_irs = {}
- for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do
- if ir_.cite_item.id ~= cite_ir.cite_item.id then
- table.insert(ambiguous_cite_irs, ir_)
- end
- if ir_.disam_str == cite_ir.disam_str then
- table.insert(ambiguous_same_output_irs, ir_)
- end
- end
-
- -- util.debug(#ambiguous_same_output_irs)
- if #ambiguous_cite_irs == 0 then
- cite_ir.is_ambiguous = false
- break
- end
-
- -- check if the cite can be (fully) disambiguated by adding names
- local can_be_disambuguated = false
- for _, ir_ in ipairs(ambiguous_cite_irs) do
- if ir_.name_ir.full_name_str ~= cite_ir.name_ir.full_name_str then
- can_be_disambuguated = true
- break
- end
- end
- -- util.debug(can_be_disambuguated)
- if not can_be_disambuguated then
- break
- end
-
- 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
- added_person_name_ir.person_name_index = #self.person_names + 1
- table.insert(self.person_names, added_person_name_ir)
- end
- local name_output = added_person_name_ir.name_output
- if not self.person_names_by_output[name_output] then
- self.person_names_by_output[name_output] = {}
- end
- self.person_names_by_output[name_output][added_person_name_ir.person_name_index] = added_person_name_ir
-
- -- Update ir output
- local inlines = ir_:flatten(disam_format)
- local disam_str = disam_format:output(inlines, context)
- -- util.debug(disam_str)
- ir_.disam_str = disam_str
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
- self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
- end
- end
-
- -- util.debug("disambiguate_add_givenname")
-
- if self.style.citation.disambiguate_add_givenname then
- local gn_disam_rule = self.style.citation.givenname_disambiguation_rule
- if gn_disam_rule == "all-names" or gn_disam_rule == "all-names-with-initials" then
- cite_ir = self:disambiguate_add_givenname_all_names(cite_ir)
- elseif gn_disam_rule == "by-cite" then
- cite_ir = self:disambiguate_add_givenname_by_cite(cite_ir)
- end
- end
-
- cite_ir.is_ambiguous = self:check_ambiguity(cite_ir)
-
- for _, ir_ in ipairs(self.disam_irs) do
- -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str)
- end
-
- end
-
-
- return cite_ir
-end
-
-function CiteProc:collect_irs_with_disambiguate_branch(ir)
- local irs_with_disambiguate_branch = {}
- if ir.children then
- for i, child_ir in ipairs(ir.children) do
- if child_ir.disambiguate_branch_ir then
- table.insert(irs_with_disambiguate_branch, child_ir)
- elseif child_ir.children then
- util.extend(irs_with_disambiguate_branch,
- self:collect_irs_with_disambiguate_branch(child_ir))
- end
- end
- end
- return irs_with_disambiguate_branch
-end
-
-function CiteProc:disambiguate_conditionals(cite_ir)
- -- util.debug(cite_ir)
-
- cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir)
-
- local disam_format = DisamStringFormat:new()
-
- while cite_ir.is_ambiguous do
- if #cite_ir.irs_with_disambiguate_branch == 0 then
- 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(self.cite_irs_by_output[cite_ir.disam_str]) do
- if ir_.disam_str == cite_ir.disam_str then
- table.insert(ambiguous_same_output_irs, ir_)
- end
- end
-
- for _, ir_ in ipairs(ambiguous_same_output_irs) do
- if #ir_.irs_with_disambiguate_branch > 0 then
- -- Disambiguation is incremental
- -- disambiguate_IncrementalExtraText.txt
- local condition_ir = ir_.irs_with_disambiguate_branch[1]
- condition_ir.children[1] = condition_ir.disambiguate_branch_ir
- condition_ir.group_var = condition_ir.disambiguate_branch_ir.group_var
- table.remove(ir_.irs_with_disambiguate_branch, 1)
- -- disambiguate_DisambiguateTrueReflectedInBibliography.txt
- ir_.reference.disambiguate = true
-
- -- Update ir output
- local inlines = ir_:flatten(disam_format)
- local disam_str = disam_format:output(inlines, context)
- -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str)
- ir_.disam_str = disam_str
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
- self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
- end
- end
-
- cite_ir.is_ambiguous = self:check_ambiguity(cite_ir)
- -- util.debug(cite_ir.is_ambiguous)
- for _, ir_ in ipairs(self.disam_irs) do
- -- util.debug(ir_.cite_item.id .. ": " .. ir_.disam_str)
- end
-
- end
- return cite_ir
-end
-
-function CiteProc:check_ambiguity(cite_ir)
- for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do
- if ir_.cite_item.id ~= cite_ir.cite_item.id then
- return true
- end
- end
- return false
-end
-
-function CiteProc:get_same_output_irs(cite_ir)
- local ambiguous_same_output_irs = {}
- for _, ir_ in pairs(self.cite_irs_by_output[cite_ir.disam_str]) do
- if ir_.disam_str == cite_ir.disam_str then
- table.insert(ambiguous_same_output_irs, ir_)
- end
- end
- return ambiguous_same_output_irs
-end
-
-function CiteProc:disambiguate_add_year_suffix(cite_ir)
- if not cite_ir.is_ambiguous or not self.style.citation.disambiguate_add_year_suffix then
- return cite_ir
- end
-
- local same_output_irs = self:get_same_output_irs(cite_ir)
-
- table.sort(same_output_irs, function (a, b)
- -- return a.ir_index < b.ir_index
- return a.reference["citation-number"] < b.reference["citation-number"]
- end)
-
- local year_suffix_number = 0
- -- util.debug(cite_ir)
-
- local disam_format = DisamStringFormat:new()
-
- for _, ir_ in ipairs(same_output_irs) do
- ir_.reference.year_suffix_number = nil
- end
-
- for _, ir_ in ipairs(same_output_irs) do
- -- print(ir_.cite_item.id)
- -- print(ir_.reference)
- if not ir_.reference.year_suffix_number then
- year_suffix_number = year_suffix_number + 1
- ir_.reference.year_suffix_number = year_suffix_number
- ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number)
- end
-
- if not ir_.year_suffix_irs then
- ir_.year_suffix_irs = self:collect_year_suffix_irs(ir_)
- if #ir_.year_suffix_irs == 0 then
- -- By default, the year-suffix is appended the first year rendered through cs:date
- local year_ir = self:find_first_year_ir(ir_)
- -- util.debug(year_ir)
- if year_ir then
- local year_suffix_ir = YearSuffix:new({}, self.style.citation)
- table.insert(year_ir.children, year_suffix_ir)
- table.insert(ir_.year_suffix_irs, year_suffix_ir)
- end
- end
- end
-
- for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do
- 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 = "important"
- end
-
- local inlines = ir_:flatten(disam_format)
- local disam_str = disam_format:output(inlines, context)
- -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str)
- ir_.disam_str = disam_str
- if not self.cite_irs_by_output[disam_str] then
- self.cite_irs_by_output[disam_str] = {}
- end
- self.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
-
- end
-
- cite_ir.is_ambiguous = false
-
- return cite_ir
-end
-
-function CiteProc:collect_year_suffix_irs(ir)
- local year_suffix_irs = {}
- if ir.children then
- for i, child_ir in ipairs(ir.children) do
- if child_ir._type == "YearSuffix" then
- table.insert(year_suffix_irs, child_ir)
- elseif child_ir.children then
- util.extend(year_suffix_irs,
- self:collect_year_suffix_irs(child_ir))
- end
- end
- end
- return year_suffix_irs
-end
-
-function CiteProc:find_first_year_ir(ir)
- if ir.is_year then
- return ir
- end
- if ir.children then
- for _, child_ir in ipairs(ir.children) do
- local year_ir = self:find_first_year_ir(child_ir)
- if year_ir then
- return year_ir
- end
- end
- end
- return nil
-end
-
-function CiteProc:render_year_suffix(year_suffix_number)
- if year_suffix_number <= 0 then
- return nil
- end
- local year_suffix = ""
- while year_suffix_number > 0 do
- local i = (year_suffix_number - 1) % 26
- 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
-
-local function find_first(ir, check)
- if check(ir) then
- return ir
- end
- if ir.children then
- for _, child in ipairs(ir.children) do
- local target_ir = find_first(child, check)
- if target_ir then
- return target_ir
- end
- end
- end
- return nil
-end
-
-function CiteProc:group_cites(irs)
- local disam_format = DisamStringFormat:new()
- for _, ir in ipairs(irs) do
- local first_names_ir = ir.first_names_ir
- if not first_names_ir then
- first_names_ir = find_first(ir, function (ir_)
- return ir_._element == "names"
- end)
- if first_names_ir then
- local inlines = first_names_ir:flatten(disam_format)
- first_names_ir.disam_str = disam_format:output(inlines, context)
- end
- ir.first_names_ir = first_names_ir
- end
- end
-
- local irs_by_name = {}
- local name_list = {}
-
- for _, ir in ipairs(irs) do
- local name_str = ""
- if ir.first_names_ir then
- name_str = ir.first_names_ir.disam_str
- end
- if not irs_by_name[name_str] then
- irs_by_name[name_str] = {}
- table.insert(name_list, name_str)
- end
- table.insert(irs_by_name[name_str], ir)
- end
-
- local grouped = {}
- for _, name_str in ipairs(name_list) do
- local irs_with_same_name = irs_by_name[name_str]
- for i, ir in ipairs(irs_with_same_name) do
- if i < #irs_with_same_name then
- ir.own_delimiter = self.style.citation.cite_group_delimiter
- end
- table.insert(grouped, ir)
- end
- end
- return grouped
-end
-
-function CiteProc:collapse_cites(irs)
- if self.style.citation.collapse == "citation-number" then
- self:collapse_cites_by_citation_number(irs)
- elseif self.style.citation.collapse == "year" then
- self:collapse_cites_by_year(irs)
- elseif self.style.citation.collapse == "year-suffix" then
- self:collapse_cites_by_year_suffix(irs)
- elseif self.style.citation.collapse == "year-suffix-ranged" then
- self:collapse_cites_by_year_suffix_ranged(irs)
- end
-end
-
-function CiteProc:collapse_cites_by_citation_number(irs)
- local cite_groups = {}
- local current_group = {}
- local previous_citation_number
- for i, ir in ipairs(irs) do
- local citation_number
- local only_citation_number_ir = self:get_only_citation_number(ir)
- if only_citation_number_ir then
- -- Other irs like locators are not rendered.
- -- collapse_CitationNumberRangesWithAffixesGrouped.txt
- citation_number = only_citation_number_ir.citation_number
- end
- if i == 1 then
- table.insert(current_group, ir)
- elseif citation_number and previous_citation_number and
- previous_citation_number + 1 == citation_number then
- table.insert(current_group, ir)
- else
- table.insert(cite_groups, current_group)
- current_group = {ir}
- end
- previous_citation_number = citation_number
- end
- table.insert(cite_groups, current_group)
-
- for _, cite_group in ipairs(cite_groups) do
- if #cite_group >= 3 then
- cite_group[1].own_delimiter = util.unicode["en dash"]
- for i = 2, #cite_group - 1 do
- cite_group[i].collapse_suppressed = true
- end
- cite_group[#cite_group].own_delimiter = self.style.citation.after_collapse_delimiter
- end
- end
-end
-
-function CiteProc:get_only_citation_number(ir)
- if ir.citation_number then
- return ir
- end
- if not ir.children then
- return nil
- end
- local only_citation_number_ir
- for _, child in ipairs(ir.children) do
- if child.group_var ~= "missing" then
- local citation_number_ir = self:get_only_citation_number(child)
- if citation_number_ir then
- if only_citation_number_ir then
- return nil
- else
- only_citation_number_ir = citation_number_ir
- end
- else
- return false
- end
- end
- end
- return only_citation_number_ir
-end
-
-function CiteProc:collapse_cites_by_year(irs)
- local cite_groups = {{}}
- local previous_name_str
- for i, ir in ipairs(irs) do
- local name_str
- if ir.first_names_ir then
- name_str = ir.first_names_ir.disam_str
- end
- if i == 1 then
- table.insert(cite_groups[#cite_groups], ir)
- elseif name_str and name_str == previous_name_str then
- -- ir.fist_names_ir was set in the cite grouping stage
- -- TODO: and not previous cite suffix
- table.insert(cite_groups[#cite_groups], ir)
- else
- table.insert(cite_groups, {ir})
- end
- previous_name_str = name_str
- end
-
- for _, cite_group in ipairs(cite_groups) do
- if #cite_group > 1 then
- for i, cite_ir in ipairs(cite_group) do
- if i > 1 and cite_ir.first_names_ir then
- cite_ir.first_names_ir.collapse_suppressed = true
- end
- if i == #cite_group then
- cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter
- elseif i < #cite_group then
- -- The delimiter depends on the citation > sort.
- -- https://github.com/citation-style-language/test-suite/issues/39#issuecomment-687901688
- if cite_ir.cite_item.locator then
- -- Special hack for
- cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter
- elseif self.style.citation.cite_grouping then
- if self.style.citation.sort then
- cite_ir.own_delimiter = self.style.citation.cite_group_delimiter
- else
- cite_ir.own_delimiter = self.style.citation.layout.delimiter
- end
- else
- if self.style.citation.sort then
- cite_ir.own_delimiter = self.style.citation.cite_group_delimiter
- else
- -- disambiguate_YearCollapseWithInstitution.txt
- -- disambiguate_InitializeWithButNoDisambiguation.txt ?
- cite_ir.own_delimiter = self.style.citation.layout.delimiter
- end
- end
- end
- end
- end
- end
-end
-
-local function find_rendered_year_suffix(ir)
- if ir._type == "YearSuffix" then
- return ir
- end
- if ir.children then
- for _, child in ipairs(ir.children) do
- if child.group_var ~= "missing" then
- local year_suffix = find_rendered_year_suffix(child)
- if year_suffix then
- return year_suffix
- end
- end
- end
- end
- return nil
-end
-
-function CiteProc:collapse_cites_by_year_suffix(irs)
- self:collapse_cites_by_year(irs)
- -- Group by disam_str
- -- The year-suffix is ommitted in DisamStringFormat
- local cite_groups = {{}}
- local previous_ir
- local previous_year_suffix
- for i, ir in ipairs(irs) do
- local year_suffix = find_rendered_year_suffix(ir)
- ir.rendered_year_suffix_ir = year_suffix
- if i == 1 then
- table.insert(cite_groups[#cite_groups], ir)
- elseif year_suffix and previous_ir.disam_str == ir.disam_str and previous_year_suffix then
- -- TODO: and not previous cite suffix
- table.insert(cite_groups[#cite_groups], ir)
- else
- table.insert(cite_groups, {ir})
- end
- previous_ir = ir
- previous_year_suffix = year_suffix
- end
-
- for _, cite_group in ipairs(cite_groups) do
- if #cite_group > 1 then
- for i, cite_ir in ipairs(cite_group) do
- if i > 1 then
- -- cite_ir.children = {cite_ir.rendered_year_suffix_ir}
- -- Set the collapse_suppressed flag rather than removing the child irs.
- -- This leaves the disamb ir structure unchanged.
- self:suppress_ir_except_child(cite_ir, cite_ir.rendered_year_suffix_ir)
- end
- if i < #cite_group then
- if self.style.citation.cite_grouping then
- -- In the current citeproc-js impplementation, explicitly set
- -- cite-group-delimiter takes precedence over year-suffix-delimiter.
- -- May be changed in the future.
- -- https://github.com/citation-style-language/test-suite/issues/50
- cite_ir.own_delimiter = self.style.citation.cite_group_delimiter
- else
- cite_ir.own_delimiter = self.style.citation.year_suffix_delimiter
- end
- elseif i == #cite_group then
- cite_ir.own_delimiter = self.style.citation.after_collapse_delimiter
- end
- end
- end
- end
-end
-
-function CiteProc:suppress_ir_except_child(ir, target)
- if ir == target then
- ir.collapse_suppressed = false
- return false
- end
- ir.collapse_suppressed = true
- if ir.children then
- for _, child in ipairs(ir.children) do
- if child.group_var ~= "missing" and not child.collapse_suppressed then
- if not self:suppress_ir_except_child(child, target) then
- ir.collapse_suppressed = false
- end
- end
- end
- end
- return ir.collapse_suppressed
-end
-
-function CiteProc:collapse_cites_by_year_suffix_ranged(irs)
- self:collapse_cites_by_year_suffix(irs)
- -- Group by disam_str
- local cite_groups = {{}}
- local previous_ir
- local previous_year_suffix
- for i, ir in ipairs(irs) do
- local year_suffix_ir = find_rendered_year_suffix(ir)
- ir.rendered_year_suffix_ir = year_suffix_ir
- if i == 1 then
- table.insert(cite_groups[#cite_groups], ir)
- elseif year_suffix_ir and previous_ir.disam_str == ir.disam_str and previous_year_suffix and
- year_suffix_ir.year_suffix_number == previous_year_suffix.year_suffix_number + 1 then
- -- TODO: and not previous cite suffix
- table.insert(cite_groups[#cite_groups], ir)
- else
- table.insert(cite_groups, {ir})
- end
- previous_ir = ir
- previous_year_suffix = year_suffix_ir
- end
-
- for _, cite_group in ipairs(cite_groups) do
- if #cite_group > 2 then
- for i, cite_ir in ipairs(cite_group) do
- if i == 1 then
- cite_ir.own_delimiter = util.unicode["en dash"]
- elseif i < #cite_group then
- cite_ir.collapse_suppressed = true
- end
- end
- end
- end
-end
-
function CiteProc:makeCitationCluster(citation_items)
+ local special_form = nil
local items = {}
+
for i, cite_item in ipairs(citation_items) do
- cite_item.id = tostring(cite_item.id)
+ cite_item = self:normalize_cite_item(cite_item)
local item_data = self:get_item(cite_item.id)
-- Create a wrapper of the orignal item from registry so that
-- it may hold different `locator` or `position` values for cites.
local item = setmetatable(cite_item, {__index = item_data})
- -- Use "page" as locator label if missing
- -- label_PluralWithAmpersand.txt
- if item.locator and not item.label then
- item.label = "page"
+ if not special_form then
+ for _, form in ipairs({"author-only", "suppress-author", "coposite"}) do
+ if cite_item[form] then
+ special_form = form
+ end
+ end
end
item.position_level = util.position_map["first"]
@@ -1618,7 +555,12 @@ function CiteProc:makeCitationCluster(citation_items)
self:sort_bibliography()
end
- local res = self:build_cluster(items)
+ local citation_element = self.style.citation
+ if special_form == "author-only" and self.style.intext then
+ citation_element = self.style.intext
+ end
+
+ local res = citation_element:build_cluster(items, self)
-- local context = {
-- build = {},
@@ -1641,42 +583,14 @@ function CiteProc:makeBibliography()
return {{}, {}}
end
- local output_format = self.output_format
-
local res = {}
self.registry.longest_label = ""
self.registry.maxoffset = 0
for _, id in ipairs(self:get_sorted_refs()) do
- local ref = self.registry.registry[id]
-
- local state = IrState:new()
- local context = Context:new()
- context.engine = self
- context.style = self.style
- context.area = self.style.bibliography
- context.in_bibliography = true
- context.locale = self:get_locale(self.lang)
- context.name_inheritance = self.style.bibliography.name_inheritance
- context.format = output_format
- context.id = id
- context.cite = nil
- context.reference = self:get_item(id)
-
- local ir = self.style.bibliography:build_ir(self, state, context)
- -- util.debug(ir)
- ir.reference = context.reference
-
- -- Add year-suffix
- self:add_bibliography_year_suffix(ir)
-
- -- The layout output may be empty: sort_OmittedBibRefNonNumericStyle.txt
- if ir then
- local flat = ir:flatten(output_format)
- local str = output_format:output_bibliography_entry(flat, context)
- table.insert(res, str)
- end
+ local str = self.style.bibliography:build_bibliography_str(id, self)
+ table.insert(res, str)
end
local bib_start = self.output_format.markups["bibstart"]
@@ -1709,34 +623,6 @@ function CiteProc:get_sorted_refs()
return self.registry.reflist
end
-function CiteProc:add_bibliography_year_suffix(ir)
- if not ir.reference.year_suffix_number then
- return
- end
-
- local year_suffix_number = ir.reference.year_suffix_number
-
- if not ir.year_suffix_irs then
- ir.year_suffix_irs = self:collect_year_suffix_irs(ir)
- if #ir.year_suffix_irs == 0 then
- local year_ir = self:find_first_year_ir(ir)
- -- util.debug(year_ir)
- if year_ir then
- local year_suffix_ir = YearSuffix:new({}, self.style.citation)
- table.insert(year_ir.children, year_suffix_ir)
- table.insert(ir.year_suffix_irs, year_suffix_ir)
- end
- end
- end
-
- for _, year_suffix_ir in ipairs(ir.year_suffix_irs) do
- year_suffix_ir.inlines = {PlainText:new(ir.reference["year-suffix"])}
- year_suffix_ir.group_var = "important"
- end
-
-
-end
-
function CiteProc:set_output_format(format)
if format == "latex" then
self.output_format = LatexWriter:new()
@@ -1746,13 +632,11 @@ function CiteProc:set_output_format(format)
end
function CiteProc:enable_linking()
- self.opt.url_link = true
- self.opt.doi_link = true
+ self.opt.wrap_url_and_doi = true
end
function CiteProc:disable_linking()
- self.opt.url_link = false
- self.opt.doi_link = false
+ self.opt.wrap_url_and_doi = false
end
function CiteProc.create_element_tree(node)
@@ -1806,6 +690,7 @@ function CiteProc:_retrieve_item(id)
return nil
end
+ -- TODO: normalize data input
item.id = tostring(item.id)
for key, value in pairs(item) do
@@ -1985,6 +870,22 @@ function CiteProc:get_style_class()
end
+local Macro = Element:derive("macro")
+
+function Macro:from_node(node)
+ local o = Macro:new()
+ o.children = {}
+ o:set_attribute(node, "name")
+ o:process_children_nodes(node)
+ return o
+end
+
+function Macro:build_ir(engine, state, context)
+ local ir = self:build_group_ir(engine, state, context)
+ return ir
+end
+
+
engine.CiteProc = CiteProc
return engine
diff --git a/biblio/citation-style-language/citeproc-ir-node.lua b/biblio/citation-style-language/citeproc-ir-node.lua
index 01cca4abf6..50fe34b059 100644
--- a/biblio/citation-style-language/citeproc-ir-node.lua
+++ b/biblio/citation-style-language/citeproc-ir-node.lua
@@ -74,6 +74,35 @@ function IrNode:capitalize_first_term()
end
end
+function IrNode:collect_year_suffix_irs()
+ local year_suffix_irs = {}
+ if self.children then
+ for i, child_ir in ipairs(self.children) do
+ if child_ir._type == "YearSuffix" then
+ table.insert(year_suffix_irs, child_ir)
+ elseif child_ir.children then
+ util.extend(year_suffix_irs,
+ child_ir:collect_year_suffix_irs())
+ end
+ end
+ end
+ return year_suffix_irs
+end
+
+function IrNode:find_first_year_ir()
+ if self.is_year then
+ return self
+ end
+ if self.children then
+ for _, child_ir in ipairs(self.children) do
+ local year_ir = child_ir:find_first_year_ir()
+ if year_ir then
+ return year_ir
+ end
+ end
+ end
+ return nil
+end
local Rendered = IrNode:derive("Rendered")
diff --git a/biblio/citation-style-language/citeproc-latex-core.lua b/biblio/citation-style-language/citeproc-latex-core.lua
index 325217862b..0fa0b40ca3 100644
--- a/biblio/citation-style-language/citeproc-latex-core.lua
+++ b/biblio/citation-style-language/citeproc-latex-core.lua
@@ -10,6 +10,7 @@ local citeproc = require("citeproc")
local bibtex -- = require("citeproc-bibtex") -- load on demand
local util = citeproc.util
require("lualibs")
+local latex_parser = require("citeproc-latex-parser")
core.locale_file_format = "csl-locales-%s.xml"
@@ -74,7 +75,13 @@ local function read_data_file(data_file)
local csl_items = nil
if extension == ".json" then
- csl_items = utilities.json.tolua(contents)
+ local status, res = pcall(utilities.json.tolua, contents)
+ if status and res then
+ csl_items = res
+ else
+ util.error(string.format('JSON decoding error in file "%s"', data_file))
+ csl_items = {}
+ end
elseif extension == ".bib" then
bibtex = bibtex or require("citeproc-bibtex")
csl_items = bibtex.parse(contents)
@@ -169,14 +176,21 @@ end
function core.make_citation(citation_info)
-- `citation_info`: "citationID={ITEM-1@2},citationItems={{id={ITEM-1},label={page},locator={6}}},properties={noteIndex={3}}"
local citation = parse_latex_prop(citation_info)
- assert(citation.citationID)
- assert(citation.citationItems)
- assert(citation.properties)
+ -- assert(citation.citationID)
+ -- assert(citation.citationItems)
+ -- assert(citation.properties)
- citation.citationItems = parse_latex_seq(citation.citationItems)
+ citation.citationItems = parse_latex_seq(citation.citationItems)
for i, item in ipairs(citation.citationItems) do
- citation.citationItems[i] = parse_latex_prop(item)
+ local citation_item = parse_latex_prop(item)
+ if citation_item.prefix then
+ citation_item.prefix = latex_parser.convert_latex_to_rich_text(citation_item.prefix)
+ end
+ if citation_item.suffix then
+ citation_item.suffix = latex_parser.convert_latex_to_rich_text(citation_item.suffix)
+ end
+ citation.citationItems[i] = citation_item
end
citation.properties = parse_latex_prop(citation.properties)
@@ -269,21 +283,37 @@ function core.make_bibliography(engine)
local res = ""
- local bib_options = ""
- if params["hangingindent"] then
- bib_options = bib_options .. "\n hanging-indent = true,"
- end
- if params["linespacing"] then
- bib_options = bib_options .. string.format("\n line-spacing = %d,", params["linespacing"])
- end
- if params["entryspacing"] then
- bib_options = bib_options .. string.format("\n entry-spacing = %d,", params["entryspacing"])
+ local bib_options = {}
+ bib_options["class"] = engine:get_style_class()
+ local bib_option_list = {"class"}
+
+ local bib_option_map = {
+ ["entry-spacing"] = "entryspacing",
+ ["line-spacing"] = "linespacing",
+ ["hanging-indent"] = "hangingindent",
+ }
+ local bib_option_order = {
+ "class",
+ "hanging-indent",
+ "line-spacing",
+ "entry-spacing",
+ }
+
+ for option, param in pairs(bib_option_map) do
+ if params[param] then
+ bib_options[option] = params[param]
+ end
end
- if bib_options ~= "" then
- bib_options = "\\cslsetup{" .. bib_options .. "\n}\n\n"
- res = res .. bib_options
+ local bib_options_str = "\\cslsetup{\n"
+ for _, option in ipairs(bib_option_order) do
+ local value = bib_options[option]
+ if value then
+ bib_options_str = bib_options_str .. string.format(" %s = %s,\n", option, tostring(value))
+ end
end
+ bib_options_str = bib_options_str .. "}\n"
+ res = res .. bib_options_str .. "\n"
-- util.debug(params.bibstart)
if params.bibstart then
diff --git a/biblio/citation-style-language/citeproc-latex-parser.lua b/biblio/citation-style-language/citeproc-latex-parser.lua
new file mode 100644
index 0000000000..1dcc2d4c77
--- /dev/null
+++ b/biblio/citation-style-language/citeproc-latex-parser.lua
@@ -0,0 +1,259 @@
+--
+-- Copyright (c) 2021-2022 Zeping Lee
+-- Released under the MIT license.
+-- Repository: https://github.com/zepinglee/citeproc-lua
+--
+
+local latex_parser = {}
+
+local lpeg = require("lpeg")
+local unicode = require("unicode")
+local bibtex_data = require("citeproc-bibtex-data")
+local util = require("citeproc-util")
+
+
+function latex_parser.get_latex_grammar()
+ 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
+
+ local space = S(" \t\r\n")^0
+ local specials = P"\\$" / "$"
+ + P"\\%" / "%"
+ + P"\\&" / "&"
+ + P"\\#" / "#"
+ + P"\\_" / "_"
+ + P"\\{" / "{"
+ + P"\\}" / "}"
+ + P"~" / util.unicode["no-break space"]
+ local control_sequence = C(P"\\" * (R("AZ", "az")^1 + 1) * space) / function (cs)
+ return {
+ type = "control_sequence",
+ name = util.rstrip(cs),
+ raw = cs,
+ }
+ end
+ local math = P"$" * C((P"\\$" + 1 - S"$")^0) * P"$" / function (math_text)
+ return {
+ type = "math",
+ contents = math_text,
+ }
+ end
+ local ligatures = P"``" / util.unicode["left double quotation mark"]
+ + P"`" / util.unicode["left single quotation mark"]
+ + P"''" / util.unicode["right double quotation mark"]
+ + P"'" / util.unicode["right single quotation mark"]
+ + P"---" / util.unicode["em dash"]
+ + P"--" / util.unicode["en dash"]
+ local plain_text = C(1 - S"{}$\\")
+ local latex_grammar = P{
+ "latex_text";
+ latex_text = Ct((specials + control_sequence + math + ligatures + specials + V"group" + plain_text)^0),
+ group = P"{" * V"latex_text" * P"}" / function (group_contents)
+ return {
+ type = "group",
+ contents = group_contents,
+ }
+ end,
+ }
+ return latex_grammar
+end
+
+
+latex_parser.latex_grammar = latex_parser.get_latex_grammar()
+
+
+function latex_parser.convert_latex_to_rich_text(str)
+ local ast = latex_parser.latex_grammar:match(str)
+ latex_parser.convert_accents_to_unicode(ast)
+ local res = latex_parser.convert_ast_to_rich_text(ast)
+ return res
+end
+
+
+local format_commands_with_argment = {
+ ["\\textbf"] = "bold",
+ ["\\textit"] = "italic",
+ ["\\textsl"] = "italic",
+ ["\\emph"] = "italic",
+ ["\\enquote"] = "quote",
+ ["\\textsc"] = "sc",
+ ["\\sout"] = "strike", -- from ulem package
+ ["\\st"] = "strike", -- from soul package
+ ["\\textsuperscript"] = "sup",
+ ["\\textsubscript"] = "sub",
+}
+
+local format_commands_without_argment = {
+ ["\\bf"] = "bold",
+ ["\\bfseries"] = "bold",
+ ["\\it"] = "italic",
+ ["\\itshape"] = "italic",
+ ["\\sl"] = "italic",
+ ["\\slshape"] = "italic",
+ ["\\em"] = "italic",
+ ["\\scshape"] = "sc",
+ ["\\sc"] = "sc",
+}
+
+
+function latex_parser.convert_accents_to_unicode(tokens)
+ local res = ""
+ local i = 1
+ while i <= #tokens do
+ if type(token) == "table" and token.type == "control_sequence" then
+ local cs = token
+ local code_point = bibtex_data.unicode_commands[cs.name]
+ if code_point then
+ local unicode_char
+ if type(code_point) == "string" then
+ unicode_char = utf8.char(tonumber(code_point, 16))
+ tokens[i] = unicode_char
+
+ elseif type(code_point) == "table" then
+ -- The command takes an argument (\"{o})
+ local arg
+ if i < #tokens then
+ local next_token = tokens[i + 1]
+ if type(next_token) == "string" then
+ arg = next_token
+ elseif type(next_token) == "table" then
+ if next_token.type == "control_sequence" then
+ arg = next_token.name
+ elseif next_token.type == "group" then
+ if #next_token.contents == 0 then
+ arg = "{}"
+ elseif #next_token.contents == 1 then
+ next_token = next_token.contents[1]
+ if type(next_token) == "string" then
+ arg = next_token
+ elseif type(next_token) == "table" then
+ arg = next_token.name
+ end
+ end
+ end
+ end
+ end
+ if arg and code_point[arg] then
+ unicode_char = utf8.char(tonumber(code_point[arg], 16))
+ tokens[i] = unicode_char
+ table.remove(tokens, i + 1)
+ end
+ end
+ end
+ end
+ i = i + 1
+ end
+ return res
+end
+
+
+
+function latex_parser.convert_ast_to_rich_text(tokens)
+ local res = {}
+
+ local tmp_str = ""
+ local i = 1
+ while i <= #tokens do
+ local token = tokens[i]
+ if type(token) == "string" then
+ tmp_str = tmp_str .. token
+ elseif type(token) == "table" then
+ if tmp_str ~= "" then
+ table.insert(res, tmp_str)
+ tmp_str = ""
+ end
+ if token.type == "control_sequence" then
+ local format_with_argment = format_commands_with_argment[token.name]
+ local format_without_argment = format_commands_without_argment[token.name]
+ if format_with_argment then
+ if i < #tokens then
+ local next_token = tokens[i + 1]
+ local rich_text
+ if type(next_token) == "string" then
+ rich_text = {[format_with_argment] = next_token}
+ elseif type(next_token) == "table" then
+ if next_token.type == "control_sequence" then
+ local content = {code = next_token.raw}
+ rich_text = {[format_with_argment] = {content}}
+ elseif next_token.type == "group" then
+ rich_text = {[format_with_argment] = latex_parser.convert_ast_to_rich_text(next_token.contents)}
+ elseif next_token.type == "math" then
+ local content = latex_parser.convert_ast_to_rich_text(next_token.contents)
+ rich_text = {[format_with_argment] = {{["math-tex"] = content}}}
+ end
+ end
+ if rich_text then
+ table.insert(res, rich_text)
+ i = i + 1
+ end
+ else
+ table.insert(res, {code = token.raw})
+ end
+
+ elseif format_without_argment then
+ local rest_tokens = {}
+ for j = i + 1, #tokens do
+ table.insert(rest_tokens, tokens[j])
+ end
+ local rich_text = {[format_without_argment] = latex_parser.convert_ast_to_rich_text(rest_tokens)}
+ table.insert(res, rich_text)
+ i = #tokens
+
+ else
+ local rich_text = {code = token.raw}
+ table.insert(res, rich_text)
+
+ end
+
+ elseif token.type == "group" then
+ table.insert(res, {code = "{"})
+ for _, rich_text in ipairs(latex_parser.convert_ast_to_rich_text(token.contents)) do
+ table.insert(res, rich_text)
+ end
+ table.insert(res, {code = "}"})
+
+ elseif token.type == "math" then
+ table.insert(res, {["math-tex"] = token.contents})
+ end
+
+ end
+ i = i + 1
+ end
+
+ if tmp_str ~= "" then
+ table.insert(res, tmp_str)
+ end
+
+ -- Merge tokens
+ for i = #res - 1, 1, -1 do
+ local token = res[i]
+ local next_token = res[i + 1]
+ local token_type = type(token)
+ local next_token_type = type(next_token)
+ if token_type == "string" and next_token_type == "string" then
+ res[i] = token .. next_token
+ table.remove(res, i + 1)
+ elseif token_type == "table" and token.code and
+ next_token_type == "table" and next_token.code then
+ token.code = token.code .. next_token.code
+ table.remove(res, i + 1)
+ end
+ end
+
+ if #res == 1 and type(res[1]) == "string" then
+ res = res[1]
+ end
+
+ return res
+end
+
+return latex_parser
diff --git a/biblio/citation-style-language/citeproc-latex.lua b/biblio/citation-style-language/citeproc-latex.lua
index 4b05f0bdd6..4b17ce35ef 100644
--- a/biblio/citation-style-language/citeproc-latex.lua
+++ b/biblio/citation-style-language/citeproc-latex.lua
@@ -87,11 +87,11 @@ end
function csl.cite(citation_info)
-- "citationID={ITEM-UNAVAILABLE@1},citationItems={{id={ITEM-UNAVAILABLE}}},properties={noteIndex={1}}"
- -- util.debug(citation_info)
if not csl.engine then
csl.error("CSL engine is not initialized.")
end
+ -- util.debub(citation_info)
local citation = core.make_citation(citation_info)
local citation_str
@@ -103,8 +103,11 @@ function csl.cite(citation_info)
citation_str = csl.engine:process_citation(citation)
end
- tex.sprint(string.format("{%s}{%s}", csl.style_class, citation_str))
+ -- util.debug(citation_str)
-- tex.sprint(citation_str)
+ -- tex.setcatcode(35, 12) -- #
+ -- tex.setcatcode(37, 12) -- %
+ token.set_macro("l__csl_citation_tl", citation_str)
table.insert(csl.citations_pre, {citation.citationID, citation.properties.noteIndex})
end
diff --git a/biblio/citation-style-language/citeproc-node-bibliography.lua b/biblio/citation-style-language/citeproc-node-bibliography.lua
new file mode 100644
index 0000000000..c6e1575a51
--- /dev/null
+++ b/biblio/citation-style-language/citeproc-node-bibliography.lua
@@ -0,0 +1,307 @@
+--
+-- Copyright (c) 2021-2022 Zeping Lee
+-- Released under the MIT license.
+-- Repository: https://github.com/zepinglee/citeproc-lua
+--
+
+local bibliography_module = {}
+
+local dom = require("luaxml-domobject")
+
+local Context = require("citeproc-context").Context
+local IrState = require("citeproc-context").IrState
+local Element = require("citeproc-element").Element
+local IrNode = require("citeproc-ir-node").IrNode
+local Rendered = require("citeproc-ir-node").Rendered
+local SeqIr = require("citeproc-ir-node").SeqIr
+local PlainText = require("citeproc-output").PlainText
+local DisamStringFormat = require("citeproc-output").DisamStringFormat
+local YearSuffix = require("citeproc-ir-node").YearSuffix
+local util = require("citeproc-util")
+
+
+local Bibliography = Element:derive("bibliography", {
+ hanging_indent = false,
+ line_spacing = 1,
+ entry_spacing = 1,
+ subsequent_author_substitute_rule = "complete-all"
+})
+
+function Bibliography:from_node(node, style)
+ local o = Bibliography:new()
+ o.children = {}
+
+ o:process_children_nodes(node)
+
+ -- o.layouts = nil -- CSL-M extension
+
+ for _, child in ipairs(o.children) do
+ local element_name = child.element_name
+ if element_name == "layout" then
+ o.layout = child
+ elseif element_name == "sort" then
+ o.sort = child
+ end
+ end
+
+ -- Whitespace
+ o:set_bool_attribute(node, "hanging-indent")
+ o:set_attribute(node, "second-field-align")
+ o:set_number_attribute(node, "line-spacing")
+ o:set_number_attribute(node, "entry-spacing")
+
+ -- Reference Grouping
+ o:set_attribute(node, "subsequent-author-substitute")
+ o:set_attribute(node, "subsequent-author-substitute-rule")
+
+ local name_inheritance = require("citeproc-node-names").Name:new()
+ for key, value in pairs(style.name_inheritance) do
+ if value ~= nil then
+ name_inheritance[key] = value
+ end
+ end
+ Element.make_name_inheritance(name_inheritance, node)
+ o.name_inheritance = name_inheritance
+
+ return o
+end
+
+function Bibliography:build_bibliography_str(id, engine)
+ 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.locale = engine:get_locale(engine.lang)
+ context.name_inheritance = self.name_inheritance
+ context.format = output_format
+ context.id = id
+ context.cite = nil
+ context.reference = engine:get_item(id)
+
+ local ir = self:build_ir(engine, state, context)
+ -- util.debug(ir)
+ ir.reference = context.reference
+
+ -- 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
+
+ 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)
+ if not self.layout then
+ util.error("Missing bibliography layout.")
+ end
+ local ir = self.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)
+ right_inline_ir.display = "right-inline"
+ if ir.affixes then
+ right_inline_ir.affixes = ir.affixes
+ right_inline_ir.formatting = ir.formatting
+ ir.affixes = nil
+ ir.formatting = nil
+ end
+ ir.children = {ir.children[1], right_inline_ir}
+ end
+
+ if self.subsequent_author_substitute then
+ self:substitute_subsequent_authors(engine, ir)
+ end
+
+ if not ir then
+ ir = Rendered:new({PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")}, self)
+ end
+ return ir
+end
+
+function Bibliography:substitute_subsequent_authors(engine, ir)
+ ir.first_name_ir = self:find_first_name_ir(ir) -- should be a SeqIr wiht _element = "names"
+ if not ir.first_name_ir then
+ engine.previous_bib_names_ir = nil
+ return
+ end
+ if self.subsequent_author_substitute_rule == "complete-all" then
+ self:substitute_subsequent_authors_complete_all(engine, ir)
+ elseif self.subsequent_author_substitute_rule == "complete-each" then
+ self:substitute_subsequent_authors_complete_each(engine, ir)
+ elseif self.subsequent_author_substitute_rule == "partial-each" then
+ self:substitute_subsequent_authors_partial_each(engine, ir)
+ elseif self.subsequent_author_substitute_rule == "partial-first" then
+ self:substitute_subsequent_authors_partial_first(engine, ir)
+ end
+ engine.previous_bib_names_ir = ir.first_name_ir
+end
+
+function Bibliography:find_first_name_ir(ir)
+ if ir._type == "NameIr" then
+ return ir
+ elseif ir.children then
+ for _, child_ir in ipairs(ir.children) do
+ local first_name_ir = self:find_first_name_ir(child_ir)
+ if first_name_ir then
+ return first_name_ir
+ end
+ end
+ end
+ return nil
+end
+
+function Bibliography:substitute_subsequent_authors_complete_all(engine, ir)
+ local bib_names_str = ""
+
+ if #ir.first_name_ir.person_name_irs > 0 then
+ for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
+ if bib_names_str ~= "" then
+ bib_names_str = bib_names_str .. " "
+ end
+ local name_variants = person_name_ir.disam_variants
+ bib_names_str = bib_names_str .. name_variants[#name_variants]
+ end
+ else
+ -- In case of a <text variable="title"/> in <substitute>
+ local disam_format = DisamStringFormat:new()
+ local inlines = ir.first_name_ir:flatten(disam_format)
+ bib_names_str = disam_format:output(inlines)
+ end
+ ir.first_name_ir.bib_names_str = bib_names_str
+
+ if engine.previous_bib_names_ir and
+ engine.previous_bib_names_ir.bib_names_str == bib_names_str then
+ local text = self.subsequent_author_substitute
+ if text == "" then
+ ir.first_name_ir.children = {}
+ ir.first_name_ir.group_var = "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
+end
+
+function Bibliography:substitute_subsequent_authors_complete_each(engine, ir)
+ local bib_names_str = ""
+
+ if #ir.first_name_ir.person_name_irs > 0 then
+ for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
+ if bib_names_str ~= "" then
+ bib_names_str = bib_names_str .. " "
+ end
+ local name_variants = person_name_ir.disam_variants
+ bib_names_str = bib_names_str .. name_variants[#name_variants]
+ end
+ else
+ -- In case of a <text variable="title"/> in <substitute>
+ local disam_format = DisamStringFormat:new()
+ local inlines = ir.first_name_ir:flatten(disam_format)
+ bib_names_str = disam_format:output(inlines)
+ end
+ ir.first_name_ir.bib_names_str = bib_names_str
+
+ if engine.previous_bib_names_ir and
+ engine.previous_bib_names_ir.bib_names_str == bib_names_str then
+ local text = self.subsequent_author_substitute
+ if #ir.first_name_ir.person_name_irs > 0 then
+ for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
+ person_name_ir.inlines = {PlainText:new(text)}
+ end
+ else
+ -- In case of a <text variable="title"/> in <substitute>
+ if text == "" then
+ ir.first_name_ir.children = {}
+ ir.first_name_ir.group_var = "missing"
+ else
+ ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)}
+ end
+ end
+ end
+end
+
+function Bibliography:substitute_subsequent_authors_partial_each(engine, ir)
+ local bib_names_str = ""
+
+ if #ir.first_name_ir.person_name_irs > 0 then
+ if engine.previous_bib_names_ir then
+ for i, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
+ local prev_name_ir = engine.previous_bib_names_ir.person_names[i]
+ if prev_name_ir then
+ local prev_name_variants = prev_name_ir.disam_variants
+ local prev_full_name_str = prev_name_variants[#prev_name_variants]
+ local name_variants = person_name_ir.disam_variants
+ local full_name_str = name_variants[#name_variants]
+ if prev_full_name_str == full_name_str then
+ local text = self.subsequent_author_substitute
+ person_name_ir.inlines = {PlainText:new(text)}
+ else
+ break
+ end
+ end
+ end
+ end
+ else
+ -- In case of a <text variable="title"/> in <substitute>
+ local disam_format = DisamStringFormat:new()
+ local inlines = ir.first_name_ir:flatten(disam_format)
+ bib_names_str = disam_format:output(inlines)
+ ir.first_name_ir.bib_names_str = bib_names_str
+ if engine.previous_bib_names_ir and
+ engine.previous_bib_names_ir.bib_names_str == bib_names_str then
+ local text = self.subsequent_author_substitute
+ if text == "" then
+ ir.first_name_ir.children = {}
+ ir.first_name_ir.group_var = "missing"
+ else
+ ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)}
+ end
+ end
+ end
+end
+
+function Bibliography:substitute_subsequent_authors_partial_first(engine, ir)
+end
+
+function Bibliography:add_bibliography_year_suffix(ir, engine)
+ if not ir.reference.year_suffix_number then
+ 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)
+ table.insert(ir.year_suffix_irs, year_suffix_ir)
+ end
+ end
+ end
+
+ for _, year_suffix_ir in ipairs(ir.year_suffix_irs) do
+ year_suffix_ir.inlines = {PlainText:new(ir.reference["year-suffix"])}
+ year_suffix_ir.group_var = "important"
+ end
+end
+
+
+bibliography_module.Bibliography = Bibliography
+
+
+return bibliography_module
diff --git a/biblio/citation-style-language/citeproc-node-citation.lua b/biblio/citation-style-language/citeproc-node-citation.lua
new file mode 100644
index 0000000000..6c7f082746
--- /dev/null
+++ b/biblio/citation-style-language/citeproc-node-citation.lua
@@ -0,0 +1,1417 @@
+
+--
+-- Copyright (c) 2021-2022 Zeping Lee
+-- Released under the MIT license.
+-- Repository: https://github.com/zepinglee/citeproc-lua
+--
+
+local citation_module = {}
+
+local dom = require("luaxml-domobject")
+
+local Context = require("citeproc-context").Context
+local IrState = require("citeproc-context").IrState
+local Element = require("citeproc-element").Element
+local IrNode = require("citeproc-ir-node").IrNode
+local Rendered = require("citeproc-ir-node").Rendered
+local SeqIr = require("citeproc-ir-node").SeqIr
+local YearSuffix = require("citeproc-ir-node").YearSuffix
+local Micro = require("citeproc-output").Micro
+local Formatted = require("citeproc-output").Formatted
+local PlainText = require("citeproc-output").PlainText
+local InlineElement = require("citeproc-output").InlineElement
+local DisamStringFormat = require("citeproc-output").DisamStringFormat
+local SortStringFormat = require("citeproc-output").SortStringFormat
+local util = require("citeproc-util")
+
+
+local Citation = Element:derive("citation", {
+ givenname_disambiguation_rule = "by-cite",
+ -- https://github.com/citation-style-language/schema/issues/338
+ -- The cite_group_delimiter may be changed to inherit the delimiter in citaion > layout.
+ cite_group_delimiter = ", ",
+ near_note_distance = 5,
+})
+
+function Citation:from_node(node, style)
+
+ local o = self:new()
+ o.children = {}
+
+ o:process_children_nodes(node)
+
+ -- o.layouts = nil -- CSL-M extension
+
+ for _, child in ipairs(o.children) do
+ local element_name = child.element_name
+ if element_name == "layout" then
+ o.layout = child
+ elseif element_name == "sort" then
+ o.sort = child
+ end
+ end
+
+ -- Disambiguation
+ o:set_bool_attribute(node, "disambiguate-add-givenname")
+ o:set_attribute(node, "givenname-disambiguation-rule")
+ o:set_bool_attribute(node, "disambiguate-add-names")
+ o:set_bool_attribute(node, "disambiguate-add-year-suffix")
+
+ -- Cite Grouping
+ o:set_attribute(node, "cite-group-delimiter")
+ -- In the current citeproc-js implementation and test suite,
+ -- cite grouping is activated by setting the cite-group-delimiter
+ -- attribute or the collapse attributes on cs:citation.
+ -- It may be changed to an independent procedure.
+ -- https://github.com/citation-style-language/schema/issues/338
+ if node:get_attribute("cite-group-delimiter") then
+ o.cite_grouping = true
+ else
+ o.cite_grouping = false
+ end
+
+
+ -- Cite Collapsing
+ o:set_attribute(node, "collapse")
+ o:set_attribute(node, "year-suffix-delimiter")
+ if not o.year_suffix_delimiter then
+ o.year_suffix_delimiter = o.layout.delimiter
+ end
+ o:set_attribute(node, "after-collapse-delimiter")
+ if not o.after_collapse_delimiter then
+ o.after_collapse_delimiter = o.layout.delimiter
+ end
+
+ -- Note Distance
+ o:set_number_attribute(node, "near-note-distance")
+
+ local name_inheritance = require("citeproc-node-names").Name:new()
+ for key, value in pairs(style.name_inheritance) do
+ if value ~= nil then
+ name_inheritance[key] = value
+ end
+ end
+ Element.make_name_inheritance(name_inheritance, node)
+ o.name_inheritance = name_inheritance
+
+ -- update_mode = "plain" or "numeric" or "position" (or "both"?)
+
+ return o
+end
+
+function Citation:build_citation_str(citation, engine)
+ if engine.registry.requires_sorting then
+ engine:sort_bibliography()
+ end
+
+ local citation_str = self:build_cluster(citation.citationItems, engine, citation.properties)
+ return citation_str
+end
+
+-- Formatting is stripped from the author-only and composite renderings
+-- of the author name
+local function remove_name_formatting(ir)
+ if ir._element == "name" then
+ ir.formatting = nil
+ end
+ if ir.children then
+ for _, child in ipairs(ir.children) do
+ remove_name_formatting(child)
+ end
+ end
+end
+
+function Citation:build_cluster(citation_items, engine, properties)
+ properties = properties or {}
+ local output_format = engine.output_format
+ local irs = {}
+ 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)
+ end
+
+ -- Special citation forms
+ -- https://citeproc-js.readthedocs.io/en/latest/running.html#special-citation-forms
+ self:_apply_special_citation_form(irs, properties, output_format, engine)
+
+ if self.cite_grouping then
+ irs = self:group_cites(irs)
+ else
+ local citation_collapse = self.collapse
+ if citation_collapse == "year" or citation_collapse == "year-suffix" or
+ citation_collapse == "year-suffix-ranged" then
+ irs = self:group_cites(irs)
+ end
+ end
+
+ if self.collapse then
+ self:collapse_cites(irs)
+ end
+
+ -- 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 prefix = citation_items[i].prefix
+ if prefix then
+ -- Prefix is inlines
+ local right_most_str = prefix[#prefix]:get_right_most_string()
+ if string.match(right_most_str, "[.!?]%s*$") and InlineElement.has_space(prefix) then
+ ir:capitalize_first_term()
+ end
+ else
+ local delimiter = self.layout.delimiter
+ if i == 1 or not delimiter or string.match(delimiter, "[.!?]%s*$") then
+ ir:capitalize_first_term()
+ end
+ end
+ end
+
+ -- util.debug(irs)
+
+ local citation_delimiter = self.layout.delimiter
+ local citation_stream = {}
+
+ local context = Context:new()
+ context.engine = engine
+ context.style = engine.style
+ context.area = self
+ context.in_bibliography = false
+ context.locale = engine:get_locale(engine.lang)
+ context.name_inheritance = self.name_inheritance
+ context.format = output_format
+
+ local previous_ir
+ for i, ir in ipairs(irs) do
+ local cite_prefix = citation_items[i].prefix
+ local cite_suffix = citation_items[i].suffix
+ if not ir.collapse_suppressed then
+ local ir_inlines = ir:flatten(output_format)
+ if #ir_inlines > 0 then
+ -- Make sure ir_inlines has outputs contents.
+ -- collapse_AuthorCollapseNoDateSorted.txt
+ if previous_ir then
+ if previous_ir.own_delimiter then
+ table.insert(citation_stream, PlainText:new(previous_ir.own_delimiter))
+ else
+ local left_most_str
+ if cite_prefix then
+ left_most_str = cite_prefix[1]:get_left_most_string()
+ end
+ if citation_delimiter and not (cite_prefix and util.startswith(left_most_str, ",")) then
+ table.insert(citation_stream, PlainText:new(citation_delimiter))
+ end
+ end
+ end
+
+ if cite_prefix then
+ table.insert(citation_stream, Micro:new(cite_prefix))
+ end
+
+ -- util.debug(ir)
+ util.extend(citation_stream, ir_inlines)
+ previous_ir = ir
+
+ if cite_suffix then
+ table.insert(citation_stream, Micro:new(cite_suffix))
+ end
+ end
+ end
+ end
+
+ local has_printed_form = true
+ if #citation_items == 0 then
+ -- bugreports_AuthorOnlyFail.txt
+ citation_stream = {PlainText:new("[NO_PRINTED_FORM]")}
+ has_printed_form = false
+ elseif #citation_stream == 0 then
+ -- date_DateNoDateNoTest.txt
+ has_printed_form = false
+ citation_stream = {PlainText:new("[CSL STYLE ERROR: reference with no printed form.]")}
+ elseif #citation_stream == 1 and citation_stream[1].value == "[NO_PRINTED_FORM]" then
+ has_printed_form = false
+ end
+
+ local author_only_mode = (properties.mode == "author-only" or
+ (#citation_items >= 1 and citation_items[1]["author-only"]))
+ if has_printed_form and context.area.layout.affixes and not author_only_mode then
+ local affixes = context.area.layout.affixes
+ if affixes.prefix then
+ table.insert(citation_stream, 1, PlainText:new(affixes.prefix))
+ end
+ if affixes.suffix then
+ table.insert(citation_stream, PlainText:new(affixes.suffix))
+ end
+ end
+
+ if has_printed_form and context.area.layout.formatting then
+ citation_stream = {Formatted:new(citation_stream, context.area.layout.formatting)}
+ end
+
+ if properties.mode == "composite" then
+ local author_ir
+ if irs[1] then
+ author_ir = irs[1].author_ir
+ end
+ if author_ir then
+ local infix = properties.infix
+ if infix then
+ if string.match(infix, "^%w") then
+ -- discretionary_SingleNarrativeCitation.txt
+ infix = " " .. infix
+ end
+ if string.match(infix, "%w$") then
+ infix = infix .. " "
+ end
+ if infix == "" then
+ -- discretionary_AuthorOnlySuppressLocator.txt
+ infix = " "
+ end
+ for i, inline in ipairs(InlineElement:parse(infix, context)) do
+ table.insert(citation_stream, i, inline)
+ end
+ else
+ table.insert(citation_stream, 1, PlainText:new(" "))
+ end
+
+ local author_inlines = author_ir:flatten(output_format)
+ for i, inline in ipairs(author_inlines) do
+ table.insert(citation_stream, i, inline)
+ end
+ end
+ end
+
+ local str = output_format:output(citation_stream, context)
+ str = util.strip(str)
+
+ return str
+end
+
+function Citation:sorted_citation_items(items, engine)
+ local citation_sort = self.sort
+ if not citation_sort then
+ return items
+ end
+
+ local state = IrState:new()
+ local context = Context:new()
+ context.engine = engine
+ context.style = engine.style
+ context.area = self
+ context.in_bibliography = false
+ context.locale = engine:get_locale(engine.lang)
+ context.name_inheritance = self.name_inheritance
+ context.format = SortStringFormat:new()
+ -- context.id = id
+ context.cite = nil
+ -- context.reference = self:get_item(id)
+
+ items = citation_sort:sort(items, state, context)
+ return items
+end
+
+function Citation:build_fully_disambiguated_ir(cite_item, output_format, engine, properties)
+ local cite_ir = self:build_ambiguous_ir(cite_item, output_format, engine)
+ -- util.debug(cite_ir)
+ cite_ir = self:apply_disambiguate_add_givenname(cite_ir, engine)
+ cite_ir = self:apply_disambiguate_add_names(cite_ir, engine)
+ cite_ir = self:apply_disambiguate_conditionals(cite_ir, engine)
+ cite_ir = self:apply_disambiguate_add_year_suffix(cite_ir, engine)
+
+ return cite_ir
+end
+
+function Citation:build_ambiguous_ir(cite_item, output_format, engine)
+ local state = IrState:new(engine.style)
+ local context = Context:new()
+ context.engine = engine
+ context.style = engine.style
+ context.area = self
+ context.locale = engine:get_locale(engine.lang)
+ context.name_inheritance = self.name_inheritance
+ context.format = output_format
+ context.id = cite_item.id
+ context.cite = cite_item
+ -- context.reference = self:get_item(cite_item.id)
+ context.reference = engine.registry.registry[cite_item.id]
+
+ local ir
+ if context.reference then
+ ir = self:build_ir(engine, state, context)
+ else
+ ir = Rendered:new({Formatted:new({PlainText:new(cite_item.id)}, {["font-weight"] = "bold"})}, self)
+ end
+
+ ir.cite_item = cite_item
+ ir.reference = context.reference
+ ir.ir_index = #engine.disam_irs + 1
+ table.insert(engine.disam_irs, ir)
+ ir.is_ambiguous = false
+ ir.disam_level = 0
+
+ -- Formattings like font-style are ignored for disambiguation.
+ local disam_format = DisamStringFormat:new()
+ local inlines = ir:flatten(disam_format)
+ local disam_str = disam_format:output(inlines, context)
+ ir.disam_str = disam_str
+
+ if not engine.cite_irs_by_output[disam_str] then
+ engine.cite_irs_by_output[disam_str] = {}
+ end
+
+ for ir_index, ir_ in pairs(engine.cite_irs_by_output[disam_str]) do
+ if ir_.cite_item.id ~= cite_item.id then
+ ir.is_ambiguous = true
+ break
+ end
+ end
+ engine.cite_irs_by_output[disam_str][ir.ir_index] = ir
+
+ return ir
+end
+
+function Citation:build_ir(engine, state, context)
+ if not self.layout then
+ util.error("Missing citation layout.")
+ end
+ return self.layout:build_ir(engine, state, context)
+end
+
+function Citation:apply_disambiguate_add_givenname(cite_ir, engine)
+ if self.disambiguate_add_givenname then
+ -- util.debug("disambiguate_add_givenname: " .. cite_ir.cite_item.id)
+
+ 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)
+ elseif gn_disam_rule == "primary-name" or gn_disam_rule == "primary-name-with-initials" then
+ cite_ir = self:apply_disambiguate_add_givenname_primary_name(cite_ir, engine)
+ elseif gn_disam_rule == "by-cite" then
+ cite_ir = self:apply_disambiguate_add_givenname_by_cite(cite_ir, engine)
+ end
+ end
+ return cite_ir
+end
+
+-- 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
+ table.insert(engine.person_names, person_name_ir)
+ end
+
+ if not engine.person_names_by_output[name_output] then
+ engine.person_names_by_output[name_output] = {}
+ end
+ engine.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir
+
+ local ambiguous_name_irs = {}
+ local 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
+ table.insert(ambiguous_name_irs, pn_ir)
+ end
+ if pn_ir.name_output == person_name_ir.name_output then
+ table.insert(ambiguous_same_output_irs, pn_ir)
+ 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
+ end
+
+ for _, pn_ir in ipairs(ambiguous_same_output_irs) do
+ -- expand one name
+ if pn_ir.disam_variants_index < #pn_ir.disam_variants then
+ pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1
+ pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index]
+ pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output]
+
+ if not engine.person_names_by_output[pn_ir.name_output] then
+ engine.person_names_by_output[pn_ir.name_output] = {}
+ end
+ engine.person_names_by_output[pn_ir.name_output][pn_ir.person_name_index] = pn_ir
+ 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
+ table.insert(ambiguous_same_output_irs, pn_ir)
+ end
+ end
+
+ end
+ end
+
+ -- update cite_ir output
+ local disam_format = DisamStringFormat:new()
+ local inlines = cite_ir:flatten(disam_format)
+ local disam_str = disam_format:output(inlines, nil)
+ cite_ir.disam_str = disam_str
+ if not engine.cite_irs_by_output[disam_str] then
+ engine.cite_irs_by_output[disam_str] = {}
+ end
+ engine.cite_irs_by_output[disam_str][cite_ir.ir_index] = cite_ir
+
+ -- 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
+ end
+ if #ambiguous_cite_irs == 0 then
+ cite_ir.is_ambiguous = false
+ end
+
+ return cite_ir
+end
+
+function Citation:apply_disambiguate_add_givenname_primary_name(cite_ir, engine)
+ if not cite_ir.person_name_irs or #cite_ir.person_name_irs == 0 then
+ return cite_ir
+ 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
+ table.insert(engine.person_names, person_name_ir)
+ end
+ if not engine.person_names_by_output[name_output] then
+ engine.person_names_by_output[name_output] = {}
+ end
+ engine.person_names_by_output[name_output][person_name_ir.person_name_index] = person_name_ir
+
+ local ambiguous_name_irs = {}
+ local 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
+ table.insert(ambiguous_name_irs, pn_ir)
+ end
+ if pn_ir.name_output == person_name_ir.name_output then
+ table.insert(ambiguous_same_output_irs, pn_ir)
+ end
+ end
+
+ for _, name_variant in ipairs(person_name_ir.disam_variants) do
+ if #ambiguous_name_irs == 0 then
+ break
+ end
+
+ for _, pn_ir in ipairs(ambiguous_same_output_irs) do
+ -- expand one name
+ if pn_ir.disam_variants_index < #pn_ir.disam_variants then
+ pn_ir.disam_variants_index = pn_ir.disam_variants_index + 1
+ pn_ir.name_output = pn_ir.disam_variants[pn_ir.disam_variants_index]
+ pn_ir.inlines = pn_ir.disam_inlines[pn_ir.name_output]
+
+ if not engine.person_names_by_output[pn_ir.name_output] then
+ engine.person_names_by_output[pn_ir.name_output] = {}
+ end
+ engine.person_names_by_output[pn_ir.name_output][person_name_ir.person_name_index] = person_name_ir
+ end
+ end
+
+ -- 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
+ table.insert(ambiguous_name_irs, pn_ir)
+ end
+ if pn_ir.name_output == person_name_ir.name_output then
+ table.insert(ambiguous_same_output_irs, pn_ir)
+ end
+ end
+ end
+
+ return cite_ir
+end
+
+function Citation:apply_disambiguate_add_givenname_by_cite(cite_ir, engine)
+ if not cite_ir.is_ambiguous then
+ return cite_ir
+ end
+ 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 = {}
+ local ambiguous_same_output_irs = {}
+
+ for ir_index, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
+ if ir_.cite_item.id ~= cite_ir.cite_item.id then
+ table.insert(ambiguous_cite_irs, ir_)
+ end
+ if ir_.disam_str == cite_ir.disam_str then
+ table.insert(ambiguous_same_output_irs, ir_)
+ end
+ 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
+ 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)
+ local disam_str = disam_format:output(inlines, nil)
+ ir_.disam_str = disam_str
+ if not engine.cite_irs_by_output[disam_str] then
+ engine.cite_irs_by_output[disam_str] = {}
+ end
+ engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
+ end
+ end
+ end
+
+ -- update ambiguous_cite_irs and ambiguous_same_output_irs
+ 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
+ if ir_.disam_str == cite_ir.disam_str then
+ table.insert(ambiguous_same_output_irs, ir_)
+ end
+ end
+
+ -- util.debug(#ambiguous_cite_irs)
+
+ if #ambiguous_cite_irs == 0 then
+ cite_ir.is_ambiguous = false
+ return cite_ir
+ end
+
+ end
+ end
+
+ return cite_ir
+end
+
+local function find_first_name_ir(ir)
+ if ir._type == "NameIr" then
+ return ir
+ end
+ if ir.children then
+ for _, child in ipairs(ir.children) do
+ local name_ir = find_first_name_ir(child)
+ if name_ir then
+ return name_ir
+ end
+ end
+ end
+ return nil
+end
+
+function Citation:apply_disambiguate_add_names(cite_ir, engine)
+ if not self.disambiguate_add_names then
+ return cite_ir
+ end
+
+ 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
+
+ if not cite_ir.is_ambiguous then
+ return cite_ir
+ end
+
+ if not name_ir or not name_ir.et_al_abbreviation then
+ return cite_ir
+ end
+
+ -- util.debug("disambiguate_add_names: " .. cite_ir.cite_item.id)
+
+ 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
+ if #cite_ir.name_ir.hidden_name_irs == 0 then
+ break
+ end
+
+ local ambiguous_cite_irs = {}
+ local ambiguous_same_output_irs = {}
+ 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
+ table.insert(ambiguous_cite_irs, ir_)
+ end
+ if ir_.disam_str == cite_ir.disam_str then
+ table.insert(ambiguous_same_output_irs, ir_)
+ end
+ end
+
+ -- util.debug(#ambiguous_same_output_irs)
+ if #ambiguous_cite_irs == 0 then
+ cite_ir.is_ambiguous = false
+ break
+ end
+
+ -- check if the cite can be (fully) disambiguated by adding names
+ local can_be_disambuguated = false
+ for _, ir_ in ipairs(ambiguous_cite_irs) do
+ if ir_.name_ir.full_name_str ~= cite_ir.name_ir.full_name_str then
+ can_be_disambuguated = true
+ break
+ end
+ end
+ -- util.debug(can_be_disambuguated)
+ if not can_be_disambuguated then
+ break
+ end
+
+ 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
+ added_person_name_ir.person_name_index = #engine.person_names + 1
+ table.insert(engine.person_names, added_person_name_ir)
+ end
+ local name_output = added_person_name_ir.name_output
+ if not engine.person_names_by_output[name_output] then
+ engine.person_names_by_output[name_output] = {}
+ end
+ engine.person_names_by_output[name_output][added_person_name_ir.person_name_index] = added_person_name_ir
+
+ -- 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] = {}
+ end
+ engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
+ end
+ end
+
+ -- util.debug("disambiguate_add_givenname")
+
+ if self.disambiguate_add_givenname then
+ 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)
+ elseif gn_disam_rule == "by-cite" then
+ cite_ir = self:apply_disambiguate_add_givenname_by_cite(cite_ir, engine)
+ end
+ end
+
+ 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
+
+function Citation:collect_irs_with_disambiguate_branch(ir)
+ local irs_with_disambiguate_branch = {}
+ if ir.children then
+ for i, child_ir in ipairs(ir.children) do
+ if child_ir.disambiguate_branch_ir then
+ table.insert(irs_with_disambiguate_branch, child_ir)
+ elseif child_ir.children then
+ util.extend(irs_with_disambiguate_branch,
+ self:collect_irs_with_disambiguate_branch(child_ir))
+ end
+ end
+ end
+ return irs_with_disambiguate_branch
+end
+
+function Citation:apply_disambiguate_conditionals(cite_ir, engine)
+ -- util.debug(cite_ir)
+
+ cite_ir.irs_with_disambiguate_branch = self:collect_irs_with_disambiguate_branch(cite_ir)
+
+ local disam_format = DisamStringFormat:new()
+
+ while cite_ir.is_ambiguous do
+ if #cite_ir.irs_with_disambiguate_branch == 0 then
+ 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
+ if ir_.disam_str == cite_ir.disam_str then
+ table.insert(ambiguous_same_output_irs, ir_)
+ end
+ end
+
+ for _, ir_ in ipairs(ambiguous_same_output_irs) do
+ if #ir_.irs_with_disambiguate_branch > 0 then
+ -- Disambiguation is incremental
+ -- disambiguate_IncrementalExtraText.txt
+ local condition_ir = ir_.irs_with_disambiguate_branch[1]
+ condition_ir.children[1] = condition_ir.disambiguate_branch_ir
+ condition_ir.group_var = condition_ir.disambiguate_branch_ir.group_var
+ table.remove(ir_.irs_with_disambiguate_branch, 1)
+ -- disambiguate_DisambiguateTrueReflectedInBibliography.txt
+ ir_.reference.disambiguate = true
+
+ -- Update ir output
+ local inlines = ir_:flatten(disam_format)
+ local disam_str = disam_format:output(inlines, nil)
+ -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str)
+ ir_.disam_str = disam_str
+ if not engine.cite_irs_by_output[disam_str] then
+ engine.cite_irs_by_output[disam_str] = {}
+ end
+ engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
+ end
+ 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
+
+function Citation:check_ambiguity(cite_ir, engine)
+ 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
+ return true
+ end
+ end
+ return false
+end
+
+function Citation:get_same_output_irs(cite_ir, engine)
+ local ambiguous_same_output_irs = {}
+ for _, ir_ in pairs(engine.cite_irs_by_output[cite_ir.disam_str]) do
+ if ir_.disam_str == cite_ir.disam_str then
+ table.insert(ambiguous_same_output_irs, ir_)
+ end
+ end
+ return ambiguous_same_output_irs
+end
+
+function Citation:apply_disambiguate_add_year_suffix(cite_ir, engine)
+ if not cite_ir.is_ambiguous or not self.disambiguate_add_year_suffix then
+ return cite_ir
+ end
+
+ local same_output_irs = self:get_same_output_irs(cite_ir, engine)
+
+ table.sort(same_output_irs, function (a, b)
+ -- return a.ir_index < b.ir_index
+ return a.reference["citation-number"] < b.reference["citation-number"]
+ end)
+
+ local year_suffix_number = 0
+ -- util.debug(cite_ir)
+
+ local disam_format = DisamStringFormat:new()
+
+ for _, ir_ in ipairs(same_output_irs) do
+ ir_.reference.year_suffix_number = nil
+ end
+
+ for _, ir_ in ipairs(same_output_irs) do
+ -- print(ir_.cite_item.id)
+ -- print(ir_.reference)
+ if not ir_.reference.year_suffix_number then
+ year_suffix_number = year_suffix_number + 1
+ ir_.reference.year_suffix_number = year_suffix_number
+ ir_.reference["year-suffix"] = self:render_year_suffix(year_suffix_number)
+ end
+
+ if not ir_.year_suffix_irs then
+ ir_.year_suffix_irs = ir_:collect_year_suffix_irs()
+ if #ir_.year_suffix_irs == 0 then
+ -- By default, the year-suffix is appended the first year rendered through cs:date
+ 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)
+ table.insert(ir_.year_suffix_irs, year_suffix_ir)
+ end
+ end
+ end
+
+ for _, year_suffix_ir in ipairs(ir_.year_suffix_irs) do
+ 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 = "important"
+ end
+
+ local inlines = ir_:flatten(disam_format)
+ local disam_str = disam_format:output(inlines, nil)
+ -- util.debug("update: " .. ir_.cite_item.id .. ": " .. disam_str)
+ ir_.disam_str = disam_str
+ if not engine.cite_irs_by_output[disam_str] then
+ engine.cite_irs_by_output[disam_str] = {}
+ end
+ engine.cite_irs_by_output[disam_str][ir_.ir_index] = ir_
+
+ end
+
+ cite_ir.is_ambiguous = false
+
+ return cite_ir
+end
+
+function Citation:render_year_suffix(year_suffix_number)
+ if year_suffix_number <= 0 then
+ return nil
+ end
+ local year_suffix = ""
+ while year_suffix_number > 0 do
+ local i = (year_suffix_number - 1) % 26
+ 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
+
+local function find_first(ir, check)
+ if check(ir) then
+ return ir
+ end
+ if ir.children then
+ for _, child in ipairs(ir.children) do
+ local target_ir = find_first(child, check)
+ if target_ir then
+ return target_ir
+ end
+ end
+ end
+ return nil
+end
+
+-- Find the first rendering element and it should be produced by and names element
+local function find_first_names_ir(ir)
+ if ir.first_names_ir then
+ return ir.first_names_ir
+ end
+
+ local first_rendering_ir = find_first(ir, function (ir_)
+ return (ir_._element == "text"
+ or ir_._element == "date"
+ or ir_._element == "number"
+ or ir_._element == "names"
+ or ir_._element == "label")
+ and ir_.group_var ~= "missing"
+ end)
+ local first_names_ir
+ if first_rendering_ir and first_rendering_ir._element == "names" then
+ first_names_ir = first_rendering_ir
+ end
+ if first_names_ir then
+ local disam_format = DisamStringFormat:new()
+ local inlines = first_names_ir:flatten(disam_format)
+ first_names_ir.disam_str = disam_format:output(inlines, nil)
+ end
+ ir.first_names_ir = first_names_ir
+ return first_names_ir
+
+end
+
+function Citation:_apply_special_citation_form(irs, properties, output_format, engine)
+ if properties.mode then
+ if properties.mode == "author-only" then
+ for _, ir in ipairs(irs) do
+ self:_apply_citation_mode_author_only(ir)
+ end
+ elseif properties.mode == "suppress-author" then
+ -- suppress-author mode does not work in note style
+ -- discretionary_FirstReferenceNumberWithIntext.txt
+ if engine.style.class ~= "note" then
+ for _, ir in ipairs(irs) do
+ self:_apply_suppress_author(ir)
+ end
+ end
+
+ elseif properties.mode == "composite" then
+ self:_apply_composite(irs[1], output_format, engine)
+
+ end
+
+ else
+ for _, ir in ipairs(irs) do
+ if ir.cite_item["author-only"] then
+ self:_apply_cite_author_only(ir)
+ elseif ir.cite_item["suppress-author"] then
+ self:_apply_suppress_author(ir)
+ end
+ end
+ end
+end
+
+function Citation:_apply_citation_mode_author_only(ir)
+ -- Used in pr
+ local author_ir = find_first_names_ir(ir)
+
+ if author_ir then
+ remove_name_formatting(author_ir)
+ ir.children = {author_ir}
+ else
+ ir.children = {Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self)}
+ end
+ return ir
+end
+
+-- Citation flags with makeCitationCluster
+-- In contrast to Citation flags with processCitationCluster, this funciton
+-- looks for the first rendering element instead of names element.
+-- See discretionary_AuthorOnly.txt
+function Citation:_apply_cite_author_only(ir)
+ local author_ir = find_first(ir, function (ir_)
+ return (ir_._element == "text"
+ or ir_._element == "date"
+ or ir_._element == "number"
+ or ir_._element == "names"
+ or ir_._element == "label")
+ and ir_.group_var ~= "missing"
+ end)
+
+ if author_ir then
+ remove_name_formatting(author_ir)
+ ir.children = {author_ir}
+ else
+ ir.children = {Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self)}
+ end
+ return ir
+end
+
+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
+end
+
+function Citation:_apply_composite(ir, output_format, engine)
+ -- local first_names_ir = find_first_names_ir(ir)
+
+ local first_names_ir = find_first_names_ir(ir)
+ if first_names_ir then
+ -- util.debug(first_names_ir)
+ first_names_ir.collapse_suppressed = true
+ end
+
+ local author_ir
+ if engine.style.intext then
+ local properties = {mode = "author-only"}
+ author_ir = engine.style.intext:build_fully_disambiguated_ir(ir.cite_item, output_format, engine, properties)
+ elseif first_names_ir then
+ author_ir = first_names_ir
+ end
+
+ if author_ir then
+ remove_name_formatting(author_ir)
+ ir.author_ir = author_ir
+ else
+ ir.author_ir = Rendered:new({PlainText:new("[NO_PRINTED_FORM]")}, self)
+ end
+
+ return ir
+end
+
+function Citation:group_cites(irs)
+ local disam_format = DisamStringFormat:new()
+ for _, ir in ipairs(irs) do
+ local first_names_ir = ir.first_names_ir
+ if not first_names_ir then
+ first_names_ir = find_first(ir, function (ir_)
+ return ir_._element == "names" and ir_.group_var ~= "missing"
+ end)
+ if first_names_ir then
+ local inlines = first_names_ir:flatten(disam_format)
+ first_names_ir.disam_str = disam_format:output(inlines, nil)
+ end
+ ir.first_names_ir = first_names_ir
+ end
+ end
+
+ local irs_by_name = {}
+ local name_list = {}
+
+ for _, ir in ipairs(irs) do
+ local name_str = ""
+ if ir.first_names_ir then
+ name_str = ir.first_names_ir.disam_str
+ end
+ if not irs_by_name[name_str] then
+ irs_by_name[name_str] = {}
+ table.insert(name_list, name_str)
+ end
+ table.insert(irs_by_name[name_str], ir)
+ end
+
+ local grouped = {}
+ for _, name_str in ipairs(name_list) do
+ local irs_with_same_name = irs_by_name[name_str]
+ for i, ir in ipairs(irs_with_same_name) do
+ if i < #irs_with_same_name then
+ ir.own_delimiter = self.cite_group_delimiter
+ end
+ table.insert(grouped, ir)
+ end
+ end
+ return grouped
+end
+
+function Citation:collapse_cites(irs)
+ if self.collapse == "citation-number" then
+ self:collapse_cites_by_citation_number(irs)
+ elseif self.collapse == "year" then
+ self:collapse_cites_by_year(irs)
+ elseif self.collapse == "year-suffix" then
+ self:collapse_cites_by_year_suffix(irs)
+ elseif self.collapse == "year-suffix-ranged" then
+ self:collapse_cites_by_year_suffix_ranged(irs)
+ end
+end
+
+function Citation:collapse_cites_by_citation_number(irs)
+ local cite_groups = {}
+ local current_group = {}
+ local previous_citation_number
+ for i, ir in ipairs(irs) do
+ local citation_number
+ local only_citation_number_ir = self:get_only_citation_number(ir)
+ if only_citation_number_ir then
+ -- Other irs like locators are not rendered.
+ -- collapse_CitationNumberRangesWithAffixesGrouped.txt
+ citation_number = only_citation_number_ir.citation_number
+ end
+ if i == 1 then
+ table.insert(current_group, ir)
+ elseif citation_number and previous_citation_number and
+ previous_citation_number + 1 == citation_number then
+ table.insert(current_group, ir)
+ else
+ table.insert(cite_groups, current_group)
+ current_group = {ir}
+ end
+ previous_citation_number = citation_number
+ end
+ table.insert(cite_groups, current_group)
+
+ for _, cite_group in ipairs(cite_groups) do
+ if #cite_group >= 3 then
+ cite_group[1].own_delimiter = util.unicode["en dash"]
+ for i = 2, #cite_group - 1 do
+ cite_group[i].collapse_suppressed = true
+ end
+ cite_group[#cite_group].own_delimiter = self.after_collapse_delimiter
+ end
+ end
+end
+
+function Citation:get_only_citation_number(ir)
+ if ir.citation_number then
+ return ir
+ end
+ if not ir.children then
+ return nil
+ end
+ local only_citation_number_ir
+ for _, child in ipairs(ir.children) do
+ if child.group_var ~= "missing" then
+ local citation_number_ir = self:get_only_citation_number(child)
+ if citation_number_ir then
+ if only_citation_number_ir then
+ return nil
+ else
+ only_citation_number_ir = citation_number_ir
+ end
+ else
+ return false
+ end
+ end
+ end
+ return only_citation_number_ir
+end
+
+function Citation:collapse_cites_by_year(irs)
+ local cite_groups = {{}}
+ local previous_name_str
+ for i, ir in ipairs(irs) do
+ local name_str
+ if ir.first_names_ir then
+ name_str = ir.first_names_ir.disam_str
+ end
+ if i == 1 then
+ table.insert(cite_groups[#cite_groups], ir)
+ elseif name_str and name_str == previous_name_str then
+ -- ir.first_names_ir was set in the cite grouping stage
+ -- TODO: and not previous cite suffix
+ table.insert(cite_groups[#cite_groups], ir)
+ else
+ table.insert(cite_groups, {ir})
+ end
+ previous_name_str = name_str
+ end
+
+ for _, cite_group in ipairs(cite_groups) do
+ if #cite_group > 1 then
+ for i, cite_ir in ipairs(cite_group) do
+ if i > 1 and cite_ir.first_names_ir then
+ cite_ir.first_names_ir.collapse_suppressed = true
+ end
+ if i == #cite_group then
+ cite_ir.own_delimiter = self.after_collapse_delimiter
+ elseif i < #cite_group then
+ -- The delimiter depends on the citation > sort.
+ -- https://github.com/citation-style-language/test-suite/issues/39#issuecomment-687901688
+ if cite_ir.cite_item.locator then
+ -- Special hack for
+ cite_ir.own_delimiter = self.after_collapse_delimiter
+ elseif self.cite_grouping then
+ if self.sort then
+ cite_ir.own_delimiter = self.cite_group_delimiter
+ else
+ cite_ir.own_delimiter = self.layout.delimiter
+ end
+ else
+ if self.sort then
+ cite_ir.own_delimiter = self.cite_group_delimiter
+ else
+ -- disambiguate_YearCollapseWithInstitution.txt
+ -- disambiguate_InitializeWithButNoDisambiguation.txt ?
+ cite_ir.own_delimiter = self.layout.delimiter
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+local function find_rendered_year_suffix(ir)
+ if ir._type == "YearSuffix" then
+ return ir
+ end
+ if ir.children then
+ for _, child in ipairs(ir.children) do
+ if child.group_var ~= "missing" then
+ local year_suffix = find_rendered_year_suffix(child)
+ if year_suffix then
+ return year_suffix
+ end
+ end
+ end
+ end
+ return nil
+end
+
+function Citation:collapse_cites_by_year_suffix(irs)
+ self:collapse_cites_by_year(irs)
+ -- Group by disam_str
+ -- The year-suffix is ommitted in DisamStringFormat
+ local cite_groups = {{}}
+ local previous_ir
+ local previous_year_suffix
+ for i, ir in ipairs(irs) do
+ local year_suffix = find_rendered_year_suffix(ir)
+ ir.rendered_year_suffix_ir = year_suffix
+ if i == 1 then
+ table.insert(cite_groups[#cite_groups], ir)
+ elseif year_suffix and previous_ir.disam_str == ir.disam_str and previous_year_suffix then
+ -- TODO: and not previous cite suffix
+ table.insert(cite_groups[#cite_groups], ir)
+ else
+ table.insert(cite_groups, {ir})
+ end
+ previous_ir = ir
+ previous_year_suffix = year_suffix
+ end
+
+ for _, cite_group in ipairs(cite_groups) do
+ if #cite_group > 1 then
+ for i, cite_ir in ipairs(cite_group) do
+ if i > 1 then
+ -- cite_ir.children = {cite_ir.rendered_year_suffix_ir}
+ -- Set the collapse_suppressed flag rather than removing the child irs.
+ -- This leaves the disamb ir structure unchanged.
+ self:suppress_ir_except_child(cite_ir, cite_ir.rendered_year_suffix_ir)
+ end
+ if i < #cite_group then
+ if self.cite_grouping then
+ -- In the current citeproc-js impplementation, explicitly set
+ -- cite-group-delimiter takes precedence over year-suffix-delimiter.
+ -- May be changed in the future.
+ -- https://github.com/citation-style-language/test-suite/issues/50
+ cite_ir.own_delimiter = self.cite_group_delimiter
+ else
+ cite_ir.own_delimiter = self.year_suffix_delimiter
+ end
+ elseif i == #cite_group then
+ cite_ir.own_delimiter = self.after_collapse_delimiter
+ end
+ end
+ end
+ end
+end
+
+function Citation:suppress_ir_except_child(ir, target)
+ if ir == target then
+ ir.collapse_suppressed = false
+ return false
+ end
+ ir.collapse_suppressed = true
+ if ir.children then
+ for _, child in ipairs(ir.children) do
+ if child.group_var ~= "missing" and not child.collapse_suppressed then
+ if not self:suppress_ir_except_child(child, target) then
+ ir.collapse_suppressed = false
+ end
+ end
+ end
+ end
+ return ir.collapse_suppressed
+end
+
+function Citation:collapse_cites_by_year_suffix_ranged(irs)
+ self:collapse_cites_by_year_suffix(irs)
+ -- Group by disam_str
+ local cite_groups = {{}}
+ local previous_ir
+ local previous_year_suffix
+ for i, ir in ipairs(irs) do
+ local year_suffix_ir = find_rendered_year_suffix(ir)
+ ir.rendered_year_suffix_ir = year_suffix_ir
+ if i == 1 then
+ table.insert(cite_groups[#cite_groups], ir)
+ elseif year_suffix_ir and previous_ir.disam_str == ir.disam_str and previous_year_suffix and
+ year_suffix_ir.year_suffix_number == previous_year_suffix.year_suffix_number + 1 then
+ -- TODO: and not previous cite suffix
+ table.insert(cite_groups[#cite_groups], ir)
+ else
+ table.insert(cite_groups, {ir})
+ end
+ previous_ir = ir
+ previous_year_suffix = year_suffix_ir
+ end
+
+ for _, cite_group in ipairs(cite_groups) do
+ if #cite_group > 2 then
+ for i, cite_ir in ipairs(cite_group) do
+ if i == 1 then
+ cite_ir.own_delimiter = util.unicode["en dash"]
+ elseif i < #cite_group then
+ cite_ir.collapse_suppressed = true
+ end
+ end
+ end
+ end
+end
+
+
+local InText = Citation:derive("intext", {
+ givenname_disambiguation_rule = "by-cite",
+ cite_group_delimiter = ", ",
+ near_note_distance = 5,
+})
+
+
+citation_module.Citation = Citation
+citation_module.InText = InText
+
+
+return citation_module
diff --git a/biblio/citation-style-language/citeproc-node-date.lua b/biblio/citation-style-language/citeproc-node-date.lua
index 7ec1f439b8..f95be07607 100644
--- a/biblio/citation-style-language/citeproc-node-date.lua
+++ b/biblio/citation-style-language/citeproc-node-date.lua
@@ -354,9 +354,9 @@ function DatePart:build_ir(single_date, engine, state, context, suppressed_affix
local inlines = {PlainText:new(text)}
local output_format = context.format
- if not context.is_english then
- print(debug.traceback())
- end
+ -- if not context.is_english then
+ -- print(debug.traceback())
+ -- end
local is_english = context:is_english()
output_format:apply_text_case(inlines, self.text_case, is_english)
inlines = output_format:with_format(inlines, self.formatting)
diff --git a/biblio/citation-style-language/citeproc-node-label.lua b/biblio/citation-style-language/citeproc-node-label.lua
index 27cafa2f18..8dc64f3c94 100644
--- a/biblio/citation-style-language/citeproc-node-label.lua
+++ b/biblio/citation-style-language/citeproc-node-label.lua
@@ -70,7 +70,9 @@ function Label:_is_variable_plural(variable, context)
return #variable > 1
elseif variable_type == "number" then
if util.startswith(variable, "number-of-") then
- return tonumber(value) > 1
+ -- Issue #27: "number-of-pages": "91–129"
+ value = string.match(tostring(value), "%d+")
+ return value and tonumber(value) > 1
else
value = tostring(value)
-- label_CollapsedPageNumberPluralDetection.txt
diff --git a/biblio/citation-style-language/citeproc-node-locale.lua b/biblio/citation-style-language/citeproc-node-locale.lua
index 0e8cdd0787..c9d9ed4259 100644
--- a/biblio/citation-style-language/citeproc-node-locale.lua
+++ b/biblio/citation-style-language/citeproc-node-locale.lua
@@ -96,7 +96,9 @@ function Locale:get_simple_term(name, form, plural)
for _, fallback_form in ipairs(self.form_fallbacks[form]) do
local key = name
if form ~= "long" then
- if not key then print(debug.traceback()) end
+ -- if not key then
+ -- print(debug.traceback())
+ -- end
key = key .. "/form-" .. fallback_form
end
local term = self.terms[key]
diff --git a/biblio/citation-style-language/citeproc-node-names.lua b/biblio/citation-style-language/citeproc-node-names.lua
index 749f571bd2..f0eb95b1e2 100644
--- a/biblio/citation-style-language/citeproc-node-names.lua
+++ b/biblio/citation-style-language/citeproc-node-names.lua
@@ -441,9 +441,6 @@ function Name:build_ir(variable, et_al, label, engine, state, context)
ir.group_var = "important"
end
- ir.formatting = self.formatting
- ir.affixes = self.affixes
-
irs = {ir}
if label then
@@ -478,6 +475,10 @@ function Name:build_person_name_ir(name, is_first, context)
local inlines = self:render_person_name(name, is_first, is_latin, is_inverted, context)
local person_name_ir = PersonNameIr:new(inlines, self)
+ -- discretionary_ExampleSeveralAuthorsWithIntext.txt
+ person_name_ir.formatting = self.formatting
+ person_name_ir.affixes = self.affixes
+
person_name_ir.is_inverted = is_inverted
local output_format = SortStringFormat:new()
@@ -1041,9 +1042,10 @@ function NamePart:format_text_case(text, context)
local output_format = context.format
local inlines = InlineElement:parse(text, context)
local is_english = context:is_english()
- if not output_format then
- print(debug.traceback())
- end
+ -- if not output_format then
+ -- print(debug.traceback())
+ -- assert(output_format)
+ -- end
output_format:apply_text_case(inlines, self.text_case, is_english)
inlines = output_format:with_format(inlines, self.formatting)
diff --git a/biblio/citation-style-language/citeproc-node-style.lua b/biblio/citation-style-language/citeproc-node-style.lua
index 5c387bd6c8..f963ed4e81 100644
--- a/biblio/citation-style-language/citeproc-node-style.lua
+++ b/biblio/citation-style-language/citeproc-node-style.lua
@@ -33,9 +33,17 @@ function Style:new()
end
function Style:parse(xml_str)
- local csl_xml = dom.parse(xml_str)
- if not csl_xml then
- error("Failed to parse CSL style.")
+ -- The parsing error is not caught by busted in some situcations and thus it's processed here.
+ -- discretionary_CitationNumberAuthorOnlyThenSuppressAuthor.txt
+ local status, csl_xml = pcall(function () return dom.parse(xml_str) end)
+ if not status or not csl_xml then
+ if csl_xml then
+ local error_message = string.match(csl_xml, "^.-: (.*)$")
+ util.error("CSL parsing error: " .. util.rstrip(error_message))
+ else
+ util.error("CSL parsing error")
+ end
+ return nil
end
local style_node = csl_xml:get_path("style")[1]
if not csl_xml then
@@ -44,33 +52,6 @@ function Style:parse(xml_str)
return Style:from_node(style_node)
end
-local function make_name_inheritance(name, node)
- name:set_attribute(node, "and")
- name:set_attribute(node, "delimiter-precedes-et-al")
- name:set_attribute(node, "delimiter-precedes-last")
- name:set_number_attribute(node, "et-al-min")
- name:set_number_attribute(node, "et-al-use-first")
- name:set_number_attribute(node, "et-al-subsequent-min")
- name:set_number_attribute(node, "et-al-subsequent-use-first")
- name:set_bool_attribute(node, "et-al-use-last")
- name:set_bool_attribute(node, "initialize")
- name:set_attribute(node, "initialize-with")
- name:set_attribute(node, "name-as-sort-order")
- name:set_attribute(node, "sort-separator")
- local delimiter = node:get_attribute("name-delimiter")
- if delimiter then
- name.delimiter = delimiter
- end
- local form = node:get_attribute("name-form")
- if form then
- name.form = form
- end
- local names_delimiter = node:get_attribute("names-delimiter")
- if names_delimiter then
- name.names_delimiter = names_delimiter
- end
-end
-
function Style:from_node(node)
local o = Style:new()
@@ -87,7 +68,7 @@ function Style:from_node(node)
-- Inheritable Name Options
-- https://docs.citationstyles.org/en/stable/specification.html#inheritable-name-options
o.name_inheritance = require("citeproc-node-names").Name:new()
- make_name_inheritance(o.name_inheritance, node)
+ Element.make_name_inheritance(o.name_inheritance, node)
if o.page_range_format == "chicago" then
if o.version < "1.1" then
@@ -111,6 +92,8 @@ function Style:from_node(node)
o.citation = child
elseif element_name == "bibliography" then
o.bibliography = child
+ elseif element_name == "intext" then
+ o.intext = child
elseif element_name == "macro" then
o.macros[child.name] = child
elseif element_name == "locale" then
@@ -197,328 +180,8 @@ function Info:from_node(node)
end
-local Citation = Element:derive("citation", {
- givenname_disambiguation_rule = "by-cite",
- -- https://github.com/citation-style-language/schema/issues/338
- -- The cite_group_delimiter may be changed to inherit the delimiter in citaion > layout.
- cite_group_delimiter = ", ",
- near_note_distance = 5,
-})
-
-function Citation:from_node(node, style)
-
- local o = Citation:new()
- o.children = {}
-
- o:process_children_nodes(node)
-
- -- o.layouts = nil -- CSL-M extension
-
- for _, child in ipairs(o.children) do
- local element_name = child.element_name
- if element_name == "layout" then
- o.layout = child
- elseif element_name == "sort" then
- o.sort = child
- end
- end
-
- -- Disambiguation
- o:set_bool_attribute(node, "disambiguate-add-givenname")
- o:set_attribute(node, "givenname-disambiguation-rule")
- o:set_bool_attribute(node, "disambiguate-add-names")
- o:set_bool_attribute(node, "disambiguate-add-year-suffix")
-
- -- Cite Grouping
- o:set_attribute(node, "cite-group-delimiter")
- -- In the current citeproc-js implementation and test suite,
- -- cite grouping is activated by setting the cite-group-delimiter
- -- attribute or the collapse attributes on cs:citation.
- -- It may be changed to an independent procedure.
- -- https://github.com/citation-style-language/schema/issues/338
- if node:get_attribute("cite-group-delimiter") then
- o.cite_grouping = true
- else
- o.cite_grouping = false
- end
-
-
- -- Cite Collapsing
- o:set_attribute(node, "collapse")
- o:set_attribute(node, "year-suffix-delimiter")
- if not o.year_suffix_delimiter then
- o.year_suffix_delimiter = o.layout.delimiter
- end
- o:set_attribute(node, "after-collapse-delimiter")
- if not o.after_collapse_delimiter then
- o.after_collapse_delimiter = o.layout.delimiter
- end
-
- -- Note Distance
- o:set_number_attribute(node, "near-note-distance")
-
- local name_inheritance = require("citeproc-node-names").Name:new()
- for key, value in pairs(style.name_inheritance) do
- if value ~= nil then
- name_inheritance[key] = value
- end
- end
- make_name_inheritance(name_inheritance, node)
- o.name_inheritance = name_inheritance
-
- -- update_mode = "plain" or "numeric" or "position" (or "both"?)
-
- return o
-end
-
-function Citation:build_ir(engine, state, context)
- if not self.layout then
- util.error("Missing citation layout.")
- end
- return self.layout:build_ir(engine, state, context)
-end
-
-
-local Bibliography = Element:derive("bibliography", {
- hanging_indent = false,
- line_spacing = 1,
- entry_spacing = 1,
- subsequent_author_substitute_rule = "complete-all"
-})
-
-function Bibliography:from_node(node, style)
- local o = Bibliography:new()
- o.children = {}
-
- o:process_children_nodes(node)
-
- -- o.layouts = nil -- CSL-M extension
-
- for _, child in ipairs(o.children) do
- local element_name = child.element_name
- if element_name == "layout" then
- o.layout = child
- elseif element_name == "sort" then
- o.sort = child
- end
- end
-
- -- Whitespace
- o:set_bool_attribute(node, "hanging-indent")
- o:set_attribute(node, "second-field-align")
- o:set_number_attribute(node, "line-spacing")
- o:set_number_attribute(node, "entry-spacing")
-
- -- Reference Grouping
- o:set_attribute(node, "subsequent-author-substitute")
- o:set_attribute(node, "subsequent-author-substitute-rule")
-
- local name_inheritance = require("citeproc-node-names").Name:new()
- for key, value in pairs(style.name_inheritance) do
- if value ~= nil then
- name_inheritance[key] = value
- end
- end
- make_name_inheritance(name_inheritance, node)
- o.name_inheritance = name_inheritance
-
- return o
-end
-
-function Bibliography:build_ir(engine, state, context)
- if not self.layout then
- util.error("Missing bibliography layout.")
- end
- local ir = self.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)
- right_inline_ir.display = "right-inline"
- if ir.affixes then
- right_inline_ir.affixes = ir.affixes
- right_inline_ir.formatting = ir.formatting
- ir.affixes = nil
- ir.formatting = nil
- end
- ir.children = {ir.children[1], right_inline_ir}
- end
-
- if self.subsequent_author_substitute then
- self:substitute_subsequent_authors(engine, ir)
- end
-
- if not ir then
- ir = Rendered:new(PlainText:new("[CSL STYLE ERROR: reference with no printed form.]"), self)
- end
- return ir
-end
-
-function Bibliography:substitute_subsequent_authors(engine, ir)
- ir.first_name_ir = self:find_first_name_ir(ir) -- should be a SeqIr wiht _element = "names"
- if not ir.first_name_ir then
- engine.previous_bib_names_ir = nil
- return
- end
- if self.subsequent_author_substitute_rule == "complete-all" then
- self:substitute_subsequent_authors_complete_all(engine, ir)
- elseif self.subsequent_author_substitute_rule == "complete-each" then
- self:substitute_subsequent_authors_complete_each(engine, ir)
- elseif self.subsequent_author_substitute_rule == "partial-each" then
- self:substitute_subsequent_authors_partial_each(engine, ir)
- elseif self.subsequent_author_substitute_rule == "partial-first" then
- self:substitute_subsequent_authors_partial_first(engine, ir)
- end
- engine.previous_bib_names_ir = ir.first_name_ir
-end
-
-function Bibliography:find_first_name_ir(ir)
- if ir._type == "NameIr" then
- return ir
- elseif ir.children then
- for _, child_ir in ipairs(ir.children) do
- local first_name_ir = self:find_first_name_ir(child_ir)
- if first_name_ir then
- return first_name_ir
- end
- end
- end
- return nil
-end
-
-function Bibliography:substitute_subsequent_authors_complete_all(engine, ir)
- local bib_names_str = ""
-
- if #ir.first_name_ir.person_name_irs > 0 then
- for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
- if bib_names_str ~= "" then
- bib_names_str = bib_names_str .. " "
- end
- local name_variants = person_name_ir.disam_variants
- bib_names_str = bib_names_str .. name_variants[#name_variants]
- end
- else
- -- In case of a <text variable="title"/> in <substitute>
- local disam_format = DisamStringFormat:new()
- local inlines = ir.first_name_ir:flatten(disam_format)
- bib_names_str = disam_format:output(inlines)
- end
- ir.first_name_ir.bib_names_str = bib_names_str
-
- if engine.previous_bib_names_ir and
- engine.previous_bib_names_ir.bib_names_str == bib_names_str then
- local text = self.subsequent_author_substitute
- if text == "" then
- ir.first_name_ir.children = {}
- ir.first_name_ir.group_var = "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
-end
-
-function Bibliography:substitute_subsequent_authors_complete_each(engine, ir)
- local bib_names_str = ""
-
- if #ir.first_name_ir.person_name_irs > 0 then
- for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
- if bib_names_str ~= "" then
- bib_names_str = bib_names_str .. " "
- end
- local name_variants = person_name_ir.disam_variants
- bib_names_str = bib_names_str .. name_variants[#name_variants]
- end
- else
- -- In case of a <text variable="title"/> in <substitute>
- local disam_format = DisamStringFormat:new()
- local inlines = ir.first_name_ir:flatten(disam_format)
- bib_names_str = disam_format:output(inlines)
- end
- ir.first_name_ir.bib_names_str = bib_names_str
-
- if engine.previous_bib_names_ir and
- engine.previous_bib_names_ir.bib_names_str == bib_names_str then
- local text = self.subsequent_author_substitute
- if #ir.first_name_ir.person_name_irs > 0 then
- for _, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
- person_name_ir.inlines = {PlainText:new(text)}
- end
- else
- -- In case of a <text variable="title"/> in <substitute>
- if text == "" then
- ir.first_name_ir.children = {}
- ir.first_name_ir.group_var = "missing"
- else
- ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)}
- end
- end
- end
-end
-
-function Bibliography:substitute_subsequent_authors_partial_each(engine, ir)
- local bib_names_str = ""
-
- if #ir.first_name_ir.person_name_irs > 0 then
- if engine.previous_bib_names_ir then
- for i, person_name_ir in ipairs(ir.first_name_ir.person_name_irs) do
- local prev_name_ir = engine.previous_bib_names_ir.person_names[i]
- if prev_name_ir then
- local prev_name_variants = prev_name_ir.disam_variants
- local prev_full_name_str = prev_name_variants[#prev_name_variants]
- local name_variants = person_name_ir.disam_variants
- local full_name_str = name_variants[#name_variants]
- if prev_full_name_str == full_name_str then
- local text = self.subsequent_author_substitute
- person_name_ir.inlines = {PlainText:new(text)}
- else
- break
- end
- end
- end
- end
- else
- -- In case of a <text variable="title"/> in <substitute>
- local disam_format = DisamStringFormat:new()
- local inlines = ir.first_name_ir:flatten(disam_format)
- bib_names_str = disam_format:output(inlines)
- ir.first_name_ir.bib_names_str = bib_names_str
- if engine.previous_bib_names_ir and
- engine.previous_bib_names_ir.bib_names_str == bib_names_str then
- local text = self.subsequent_author_substitute
- if text == "" then
- ir.first_name_ir.children = {}
- ir.first_name_ir.group_var = "missing"
- else
- ir.first_name_ir.children = {Rendered:new({PlainText:new(text)}, self)}
- end
- end
- end
-end
-
-function Bibliography:substitute_subsequent_authors_partial_first(engine, ir)
-end
-
-local Macro = Element:derive("macro")
-
-function Macro:from_node(node)
- local o = Macro:new()
- o.children = {}
- o:set_attribute(node, "name")
- o:process_children_nodes(node)
- return o
-end
-
-function Macro:build_ir(engine, state, context)
- local ir = self:build_group_ir(engine, state, context)
- return ir
-end
-
style_module.Style = Style
-style_module.Citation = Citation
-style_module.Bibliography = Bibliography
return style_module
diff --git a/biblio/citation-style-language/citeproc-node-text.lua b/biblio/citation-style-language/citeproc-node-text.lua
index fae3535013..c7275b39be 100644
--- a/biblio/citation-style-language/citeproc-node-text.lua
+++ b/biblio/citation-style-language/citeproc-node-text.lua
@@ -90,10 +90,12 @@ function Text:build_variable_ir(engine, state, context)
end
local inlines
- if (variable == "URL" and engine.opt.url_link) or
- (variable == "DOI" and engine.opt.doi_link) or
- (variable == "PMID" and engine.opt.doi_link) or
- (variable == "PMID" and engine.opt.doi_link) then
+ -- if not engine.opt then
+ -- print(debug.traceback())
+ -- end
+ -- if engine.opt.wrap_url_and_doi and (variable == "URL" or variable == "DOI" or
+ -- variable == "PMID" or variable == "PMID") then
+ if variable == "URL" or variable == "DOI" or variable == "PMID" or variable == "PMID" then
inlines = self:render_linked(engine, state, context, variable, text)
else
inlines = self:render_text_inlines(text, context)
@@ -116,14 +118,13 @@ end
function Text:render_linked(engine, state, context, variable, text)
local href
+ local url_prefix = false -- The prefix is used as part of the URL.
if variable == "URL" then
href = text
- elseif self.affixes and self.affixes.prefix then
- if string.match(self.affixes.prefix, "https?://") then
- text = self.affixes.prefix .. text
- self.affixes.prefix = nil
- href = text
- end
+ elseif self.affixes and self.affixes.prefix and string.match(self.affixes.prefix, "https?://") then
+ text = self.affixes.prefix .. text
+ href = text
+ url_prefix = true
elseif variable == "DOI" then
href = "https://doi.org/" .. text
elseif variable == "PMID" then
@@ -131,6 +132,7 @@ function Text:render_linked(engine, state, context, variable, text)
elseif variable == "PMCID" then
href = "https://www.ncbi.nlm.nih.gov/pmc/articles/" .. text
end
+
local inlines = {Linked:new(text, href)}
local output_format = context.format
local localized_quotes = nil
@@ -139,6 +141,9 @@ function Text:render_linked(engine, state, context, variable, text)
end
inlines = output_format:with_format(inlines, self.formatting)
inlines = output_format:affixed_quoted(inlines, self.affixes, localized_quotes)
+ if url_prefix then
+ table.remove(inlines, 1)
+ end
return output_format:with_display(inlines, self.display)
end
diff --git a/biblio/citation-style-language/citeproc-nodes.lua b/biblio/citation-style-language/citeproc-nodes.lua
index 69e87739b4..de01cc09d8 100644
--- a/biblio/citation-style-language/citeproc-nodes.lua
+++ b/biblio/citation-style-language/citeproc-nodes.lua
@@ -5,6 +5,9 @@
--
local style = require("citeproc-node-style")
+local citation = require("citeproc-node-citation")
+local citation = require("citeproc-node-citation")
+local bibliography = require("citeproc-node-bibliography")
local locale = require("citeproc-node-locale")
local layout = require("citeproc-node-layout")
local text = require("citeproc-node-text")
@@ -18,8 +21,9 @@ local sort = require("citeproc-node-sort")
local nodes = {
["style"] = style.Style,
- ["citation"] = style.Citation,
- ["bibliography"] = style.Bibliography,
+ ["citation"] = citation.Citation,
+ ["intext"] = citation.Intext,
+ ["bibliography"] = bibliography.Bibliography,
["locale"] = locale.Locale,
["term"] = locale.Term,
["layout"] = layout.Layout,
diff --git a/biblio/citation-style-language/citeproc-output.lua b/biblio/citation-style-language/citeproc-output.lua
index 97cc53fc4a..d3ef1d5ede 100644
--- a/biblio/citation-style-language/citeproc-output.lua
+++ b/biblio/citation-style-language/citeproc-output.lua
@@ -110,6 +110,36 @@ function Quoted:new(inlines, localized_quotes)
end
+local Code = InlineElement:derive("Code")
+
+function Code:new(value)
+ local o = InlineElement.new(self)
+ o.value = value
+ setmetatable(o, self)
+ return o
+end
+
+
+local MathML = InlineElement:derive("MathML")
+
+function MathML:new(value)
+ local o = InlineElement.new(self)
+ o.value = value
+ setmetatable(o, self)
+ return o
+end
+
+
+local MathTeX = InlineElement:derive("MathTeX")
+
+function MathTeX:new(value)
+ local o = InlineElement.new(self)
+ o.value = value
+ setmetatable(o, self)
+ return o
+end
+
+
local NoCase = InlineElement:derive("NoCase")
function NoCase:new(inlines)
@@ -152,11 +182,95 @@ function Div:new(inlines, display)
end
-function InlineElement:parse(str, context)
- -- Return a list of inlines
- if type(str) ~= "string" then
- print(debug.traceback())
+function InlineElement:parse(text, context)
+ local text_type = type(text)
+ local inlines
+ if text_type == "table" then
+ -- CSL rich text
+ inlines = self:parse_csl_rich_text(text)
+ elseif text_type == "string" then
+ -- String with HTML-like formatting tags
+ inlines = self:parse_html_tags(text, context)
+ elseif text_type == "number" then
+ inlines = {PlainText:new(tostring(text))}
+ else
+ util.error("Invalid text type")
+ end
+ return inlines
+end
+
+function InlineElement:parse_csl_rich_text(text)
+ -- Example: [
+ -- "A title with a",
+ -- {
+ -- "quote": "quoted string."
+ -- }
+ -- ]
+ local inlines = {}
+
+ local text_type = type(text)
+ if text_type == "string" then
+ table.insert(inlines, PlainText:new(text))
+ elseif text_type == "table" then
+ for _, subtext in ipairs(text) do
+ local subtext_type = type(subtext)
+ local inline
+ if subtext_type == "string" then
+ inline = PlainText:new(subtext)
+ elseif subtext_type == "table" then
+ local format
+ local content
+ for format_, content_ in pairs(subtext) do
+ format = format_
+ content = content_
+ end
+ if format == "bold" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["font-weight"] = "bold"})
+ elseif format == "code" then
+ if type(content) ~= "string" then
+ util.error("Invalid rich text content.")
+ end
+ inline = Code:new(content)
+ elseif format == "italic" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["font-style"] = "italic"})
+ elseif format == "math-ml" then
+ if type(content) ~= "string" then
+ util.error("Invalid rich text content.")
+ end
+ inline = Code:new(content)
+ elseif format == "math-tex" then
+ if type(content) ~= "string" then
+ util.error("Invalid rich text content.")
+ end
+ inline = Code:new(content)
+ elseif format == "preserve" then
+ inline = NoCase:new(self:parse_csl_rich_text(content))
+ elseif format == "quote" then
+ inline = Quoted:new(self:parse_csl_rich_text(content))
+ elseif format == "sc" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"})
+ elseif format == "strike" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["strike-through"] = true})
+ elseif format == "sub" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"})
+ elseif format == "sup" then
+ inline = Formatted:new(self:parse_csl_rich_text(content), {["font-variant"] = "small-caps"})
+ end
+ end
+ table.insert(inlines, inline)
+ end
+ else
+ util.error("Invalid text type")
end
+
+ return inlines
+end
+
+function InlineElement:parse_html_tags(str, context)
+ -- Return a list of inlines
+ -- if type(str) ~= "string" then
+ -- print(debug.traceback())
+ -- end
local html_str = "<div>" .. str .. "</div>"
local ok, html = pcall(dom.parse, html_str)
local inlines
@@ -219,7 +333,12 @@ function InlineElement:parse_quotes(inlines, context)
local quote_stack = {}
local text_stack = {{}}
- local localized_quotes = context:get_localized_quotes()
+ local localized_quotes
+ if context then
+ localized_quotes = context:get_localized_quotes()
+ else
+ localized_quotes = LocalizedQuotes:new()
+ end
for _, fragment in ipairs(quote_fragments) do
local top_text_list = text_stack[#text_stack]
@@ -287,7 +406,7 @@ function InlineElement:parse_quotes(inlines, context)
local elements = text_stack[1]
if #text_stack > 1 then
- assert(#text_stack == #quote_stack + 1)
+ -- assert(#text_stack == #quote_stack + 1)
for i, quote in ipairs(quote_stack) do
if quote == "'" then
quote = util.unicode["apostrophe"]
@@ -428,8 +547,10 @@ function InlineElement:capitalize_first_term()
-- util.debug(self)
if self._type == "PlainText" then
self.value = util.capitalize(self.value)
- elseif self.inlines[1] then
+ elseif self._type ~= "Code" and self._type ~= "MathML" and self._type ~= "MathTeX" then
+ if self.inlines[1] then
self.inlines[1]:capitalize_first_term()
+ end
end
end
@@ -461,9 +582,9 @@ function OutputFormat:flatten_ir(ir)
end
function OutputFormat:flatten_seq_ir(ir)
- if not ir.children then
- print(debug.traceback())
- end
+ -- if not ir.children then
+ -- print(debug.traceback())
+ -- end
if #ir.children == 0 then
return {}
end
@@ -563,6 +684,9 @@ function OutputFormat:apply_text_case_inner(inlines, text_case, seen_one, is_upp
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
@@ -813,6 +937,11 @@ function OutputFormat:flip_flop(inlines, state)
end
end
+ elseif inline._type == "Code" or
+ inline._type == "MathML" or
+ inline._type == "MathTeX" then
+ return
+
elseif inline.inlines then -- Div, ...
self:flip_flop(inline.inlines, state)
end
@@ -869,6 +998,11 @@ function OutputFormat:flip_flop_micro_inlines(inlines, state)
end
end
+ elseif inline._type == "Code" or
+ inline._type == "MathML" or
+ inline._type == "MathTeX" then
+ return
+
elseif inline.inlines then -- Div, ...
self:flip_flop_micro_inlines(inline.inlines, state)
end
@@ -876,14 +1010,11 @@ function OutputFormat:flip_flop_micro_inlines(inlines, state)
end
local function find_left(inline)
- if not inline then
- print(debug.traceback())
- end
if inline._type == "PlainText" then
return inline
-- elseif inline._type == "Micro" then
-- return nil
- elseif inline.inlines and inline._type~="Quoted" then
+ elseif inline.inlines and #inline.inlines > 0 and inline._type~="Quoted" then
return find_left(inline.inlines[1])
else
return nil
@@ -1158,6 +1289,15 @@ function Markup:write_inline(inline, context)
elseif inline._type == "Linked" then
return self:write_link(inline, context)
+ elseif inline._type == "Code" then
+ return self:write_code(inline.inlines)
+
+ elseif inline._type == "MathML" then
+ return self:write_mathml(inline.inlines)
+
+ elseif inline._type == "MathTeX" then
+ return self:write_math_tex(inline.inlines)
+
elseif inline._type == "NoCase" or inline._type == "NoDecor" then
return self:write_inlines(inline.inlines)
@@ -1286,12 +1426,28 @@ end
function LatexWriter:write_link(inline, context)
if inline.href == inline.value then
+ -- URL
return string.format("\\url{%s}", inline.value)
+ elseif context.engine.opt.wrap_url_and_doi then
+ return string.format("\\href{%s}{%s}", inline.href, self:write_escaped(inline.value, context))
else
- return string.format("\\href{%s}{%s}", inline.href, inline.value)
+ return self:write_escaped(inline.value, context)
end
end
+function LatexWriter:write_code(inline, context)
+ return inline.value
+end
+
+function LatexWriter:write_mathml(inline, context)
+ util.error("MathML is not supported in LaTeX output.")
+ return ""
+end
+
+function LatexWriter:write_math_tex(inline, context)
+ return string.format("$%s$", inline.value)
+end
+
local HtmlWriter = Markup:new()
@@ -1371,7 +1527,25 @@ function HtmlWriter:write_display(inline, context)
end
function HtmlWriter:write_link(inline, context)
- return string.format('<a href="%s">%s</a>', inline.href, inline.value)
+ local content = self:write_escaped(inline.value, context)
+ if context.engine.opt.wrap_url_and_doi then
+ local href = self:write_escaped(inline.href, context)
+ return string.format('<a href="%s">%s</a>', href, content)
+ else
+ return content
+ end
+end
+
+function HtmlWriter:write_code(inline, context)
+ return string.format("<code>%s</code>", inline.value)
+end
+
+function HtmlWriter:write_mathml(inline, context)
+ return string.format('<math xmlns="http://www.w3.org/1998/Math/MathML">%s</math>', inline.value)
+end
+
+function HtmlWriter:write_math_tex(inline, context)
+ return string.format("<code>$%s$</code>", self:write_escaped(inline.value, context))
end
@@ -1432,9 +1606,9 @@ function DisamStringFormat:flatten_ir(ir)
end
function DisamStringFormat:flatten_seq_ir(ir)
- if not ir.children then
- print(debug.traceback())
- end
+ -- if not ir.children then
+ -- print(debug.traceback())
+ -- end
if #ir.children == 0 then
return {}
end
@@ -1455,6 +1629,18 @@ function DisamStringFormat:flatten_seq_ir(ir)
end
+function InlineElement.has_space(inlines)
+ local str = DisamStringFormat:output(inlines)
+ if not str then
+ return false
+ end
+ if string.match(util.strip(str), "%s") then
+ return true
+ else
+ return false
+ end
+end
+
output_module.move_in_puncts = {
["."] = true,
["!"] = true,
diff --git a/biblio/citation-style-language/citeproc-util.lua b/biblio/citation-style-language/citeproc-util.lua
index 71221e6bec..1602511d8e 100644
--- a/biblio/citation-style-language/citeproc-util.lua
+++ b/biblio/citation-style-language/citeproc-util.lua
@@ -11,6 +11,21 @@ local inspect -- only load it when debugging
local util = {}
+-- Deep copy
+function util.deep_copy(obj)
+ local res
+ if type(obj) == "table" then
+ res = {}
+ for key, value in pairs(obj) do
+ res[key] = util.deep_copy(value)
+ end
+ else
+ res = obj
+ end
+ return res
+end
+
+-- Shallow copy
function util.clone(obj)
if type(obj) == "table" then
local res = {}
@@ -56,7 +71,7 @@ function util.to_list(str)
end
function util.to_ordinal (n)
- assert(type(n) == "number")
+ -- assert(type(n) == "number")
local last_digit = n % 10
if last_digit == 1 and n ~= 11
then return tostring(n) .. "st"
@@ -237,9 +252,9 @@ end
-- Python list.extend()
function util.extend(first, second)
- if not second then
- print(debug.traceback())
- end
+ -- if not second then
+ -- print(debug.traceback())
+ -- end
local l = #first
for i, element in ipairs(second) do
first[l + i] = element
@@ -262,17 +277,17 @@ end
function util.lstrip (str)
if not str then
- return nil
+ error("Invalid input")
end
- local res = string.gsub(str, "^%s+", "")
+ local res = string.gsub(str, "^%s*", "")
return res
end
function util.rstrip (str)
if not str then
- return nil
+ error("Invalid input")
end
- local res = string.gsub(str, "%s+$", "")
+ local res = string.gsub(str, "%s*$", "")
return res
end
@@ -280,16 +295,16 @@ function util.strip (str)
return util.lstrip(util.rstrip(str))
end
-function util.startswith (str, prefix)
- if not str then
- return false
+function util.startswith(str, prefix)
+ if not str or type(str) ~= "string" then
+ print(debug.traceback())
end
return string.sub(str, 1, #prefix) == prefix
end
-function util.endswith (str, suffix)
- if not str then
- return false
+function util.endswith(str, suffix)
+ if not str or type(str) ~= "string" then
+ print(debug.traceback())
end
return string.sub(str, -#suffix) == suffix
end
@@ -307,7 +322,7 @@ function util.is_numeric (str)
if not string.match(w, "^%a*%d+%a*$") and
not string.match(w, "^[MDCLXVI]+$") and
not string.match(w, "^[mdclxvi]+$") then
- -- Roman number withou validation
+ -- Roman number without validation
return false
end
end
@@ -828,7 +843,7 @@ function util.has_cjk_char(s)
end
function util.convert_roman (number)
- assert(type(number) == "number")
+ -- assert(type(number) == "number")
local output = {}
for _, tuple in ipairs(util.roman_numerals) do
local letter, value = table.unpack(tuple)
@@ -979,9 +994,9 @@ util.superscripts = {
-- File IO
function util.read_file(path)
- if not path then
- print(debug.traceback())
- end
+ -- if not path then
+ -- print(debug.traceback())
+ -- end
local file = io.open(path, "r")
if not file then
-- util.error(string.format('Cannot read file "%s".', path))
diff --git a/biblio/citation-style-language/citeproc.lua b/biblio/citation-style-language/citeproc.lua
index a4b0424530..b67b5b6477 100644
--- a/biblio/citation-style-language/citeproc.lua
+++ b/biblio/citation-style-language/citeproc.lua
@@ -9,7 +9,7 @@ local citeproc = {}
local engine = require("citeproc-engine")
local util = require("citeproc-util")
-citeproc.__VERSION__ = "0.2.2"
+citeproc.__VERSION__ = "0.3.0"
citeproc.new = engine.CiteProc.new
citeproc.util = util