298 lines
10 KiB
Lua
298 lines
10 KiB
Lua
--[[
|
||
affiliation-blocks – generate title components
|
||
Copyright © 2017–2021 Albert Krewinkel
|
||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||
with or without fee is hereby granted, provided that the above copyright notice
|
||
and this permission notice appear in all copies.
|
||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
||
OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
|
||
THIS SOFTWARE.
|
||
]]
|
||
|
||
local from_utils = require("utils")
|
||
local has_key = from_utils.has_key
|
||
local List = require("pandoc.List")
|
||
local utils = require("pandoc.utils")
|
||
local stringify = utils.stringify
|
||
|
||
local M = {}
|
||
|
||
local default_marks = {
|
||
corresponding_author = FORMAT == "latex" and { pandoc.RawInline("latex", "*") } or { pandoc.Str("*") },
|
||
equal_contributor = FORMAT == "latex" and { pandoc.RawInline("latex", "\\#") } or { pandoc.Str("#") },
|
||
}
|
||
|
||
local function get_orcid_mark(orcid_value)
|
||
if not orcid_value then
|
||
return {}
|
||
end
|
||
local orcid_str
|
||
if type(orcid_value) == "string" then
|
||
orcid_str = orcid_value
|
||
elseif type(orcid_value) == "table" then
|
||
if orcid_value.text then
|
||
orcid_str = orcid_value.text
|
||
elseif orcid_value[1] and orcid_value[1].text then
|
||
orcid_str = orcid_value[1].text
|
||
else
|
||
return {}
|
||
end
|
||
else
|
||
return {}
|
||
end
|
||
orcid_str = orcid_str:gsub("[%-%s]", "")
|
||
if FORMAT == "latex" then
|
||
return { pandoc.RawInline("latex", "\\orcidlink{" .. orcid_str .. "}") }
|
||
elseif FORMAT:match("docx") then
|
||
local orcid_url = "https://orcid.org/" .. orcid_str
|
||
return {
|
||
pandoc.Str(" "),
|
||
pandoc.Link("ID", orcid_url),
|
||
}
|
||
else
|
||
local orcid_url = "https://orcid.org/" .. orcid_str
|
||
return { pandoc.Link(pandoc.Str(""), orcid_url, "", { class = "orcid" }) }
|
||
end
|
||
end
|
||
|
||
M.default_marks = default_marks
|
||
|
||
local function is_equal_contributor(author)
|
||
if has_key(author, "attributes") then
|
||
return author.attributes["equal-contributor"]
|
||
end
|
||
return nil
|
||
end
|
||
|
||
local function create_equal_contributors_block(authors, mark)
|
||
local has_equal_contribs = List:new(authors):find_if(is_equal_contributor)
|
||
if not has_equal_contribs then
|
||
return nil
|
||
end
|
||
local contributors = {
|
||
pandoc.Superscript(mark("equal_contributor")),
|
||
pandoc.Space(),
|
||
pandoc.Str("These authors contributed equally to this work."),
|
||
}
|
||
return List:new({ pandoc.Para(contributors) })
|
||
end
|
||
M.create_equal_contributors_block = create_equal_contributors_block
|
||
|
||
local function intercalate(lists, elem)
|
||
local result = List:new({})
|
||
for i = 1, (#lists - 1) do
|
||
result:extend(lists[i])
|
||
result:extend(elem)
|
||
end
|
||
if #lists > 0 then
|
||
result:extend(lists[#lists])
|
||
end
|
||
return result
|
||
end
|
||
|
||
local function is_corresponding_author(author)
|
||
if has_key(author, "attributes") then
|
||
if author.attributes["corresponding"] then
|
||
return author.email
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
M.is_corresponding_author = is_corresponding_author
|
||
|
||
local function create_correspondence_blocks(authors, mark)
|
||
local corresponding_authors = List:new({})
|
||
for _, author in ipairs(authors) do
|
||
if is_corresponding_author(author) then
|
||
local mailto = "mailto:" .. stringify(author.email)
|
||
local author_with_mail = List:new(
|
||
author.name.literal
|
||
.. List:new({ pandoc.Space(), pandoc.Str("(") })
|
||
.. author.email
|
||
.. List:new({ pandoc.Str(")") })
|
||
)
|
||
local link = pandoc.Link(author_with_mail, mailto)
|
||
table.insert(corresponding_authors, { link })
|
||
end
|
||
end
|
||
if #corresponding_authors == 0 then
|
||
return nil
|
||
end
|
||
local correspondence = List:new({
|
||
pandoc.Superscript(mark("corresponding_author")),
|
||
pandoc.Space(),
|
||
pandoc.Str("Corresponding to:"),
|
||
pandoc.Space(),
|
||
})
|
||
local sep = List:new({ pandoc.Str(","), pandoc.Space() })
|
||
return { pandoc.Para(correspondence .. intercalate(corresponding_authors, sep)) }
|
||
end
|
||
|
||
M.create_correspondence_blocks = create_correspondence_blocks
|
||
|
||
local function author_inline_generator(get_mark, meta)
|
||
return function(author)
|
||
local author_marks = List:new({})
|
||
if has_key(author, "orcid") then
|
||
author_marks[#author_marks + 1] = get_orcid_mark(author.orcid)
|
||
end
|
||
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
|
||
for _, idx in ipairs(author.affiliations) do
|
||
local idx_num = tonumber(stringify(idx)) -- Convert MetaString/MetaInlines to number
|
||
if not idx_num then
|
||
error("Invalid affiliation index: " .. tostring(idx))
|
||
end
|
||
local idx_str
|
||
if affilstyle == "number" then
|
||
idx_str = tostring(idx_num)
|
||
else
|
||
if idx_num > 26 then
|
||
error("Too many affiliations: only up to 26 (a-z) are supported")
|
||
end
|
||
idx_str = string.char(96 + idx_num)
|
||
end
|
||
author_marks[#author_marks + 1] = { pandoc.Str(idx_str) }
|
||
end
|
||
if has_key(author, "attributes") then
|
||
if author.attributes["equal-contributor"] then
|
||
author_marks[#author_marks + 1] = get_mark("equal_contributor")
|
||
end
|
||
end
|
||
if is_corresponding_author(author) then
|
||
author_marks[#author_marks + 1] = get_mark("corresponding_author")
|
||
end
|
||
if FORMAT:match("latex") then
|
||
author.name.literal[#author.name.literal + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
|
||
return author
|
||
else
|
||
local res = List.clone(author.name.literal)
|
||
res[#res + 1] = pandoc.Superscript(intercalate(author_marks, { pandoc.Str(",") }))
|
||
return res
|
||
end
|
||
end
|
||
end
|
||
M.author_inline_generator = author_inline_generator
|
||
|
||
local function create_authors_inlines(authors, mark, meta)
|
||
local inlines_generator = author_inline_generator(mark, meta)
|
||
local inlines = List:new(authors):map(inlines_generator)
|
||
local and_str = List:new({ pandoc.Space(), pandoc.Str("and"), pandoc.Space() })
|
||
local last_author = inlines[#inlines]
|
||
inlines[#inlines] = nil
|
||
local result = intercalate(inlines, { pandoc.Str(","), pandoc.Space() })
|
||
if #authors > 1 then
|
||
if #authors == 2 then
|
||
result:extend(and_str)
|
||
else
|
||
result:extend(List:new({ pandoc.Str(",") }) .. and_str)
|
||
end
|
||
end
|
||
result:extend(last_author)
|
||
return result
|
||
end
|
||
M.create_authors_inlines = create_authors_inlines
|
||
|
||
local function create_affiliations_blocks_alphabeta(affiliations, meta)
|
||
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
|
||
local affil_lines = List:new(affiliations):map(function(affil, i)
|
||
if affilstyle == "number" then
|
||
num_inlines = pandoc.List:new({
|
||
pandoc.Superscript(pandoc.Str(tostring(i))),
|
||
pandoc.Space(),
|
||
})
|
||
else
|
||
num_inlines = pandoc.List:new({
|
||
pandoc.Superscript(pandoc.Str(string.char(96 + i))),
|
||
pandoc.Space(),
|
||
})
|
||
end
|
||
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
|
||
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
|
||
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
|
||
or { pandoc.Str(tostring(affil["postal-code"])) }
|
||
local country_inlines = type(affil.country) == "table" and affil.country
|
||
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
|
||
return num_inlines
|
||
:extend(name_inlines)
|
||
:extend({ pandoc.Str(", ") })
|
||
:extend(city_inlines)
|
||
:extend({ pandoc.Space() })
|
||
:extend(postcode_inlines)
|
||
:extend({ pandoc.Str(", ") })
|
||
:extend(country_inlines)
|
||
:extend({ pandoc.Str(".") })
|
||
end)
|
||
local combined_inlines = pandoc.List:new()
|
||
for i, line in ipairs(affil_lines) do
|
||
combined_inlines:extend(line)
|
||
if i < #affil_lines then
|
||
combined_inlines:extend({ pandoc.LineBreak() })
|
||
end
|
||
end
|
||
return { pandoc.Para(combined_inlines) }
|
||
end
|
||
|
||
local function create_affiliations_blocks_number(affiliations, meta)
|
||
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
|
||
local affil_lines = List:new(affiliations):map(function(affil, i)
|
||
local num_inlines = pandoc.List:new({
|
||
pandoc.Superscript(pandoc.Str(tostring(i))),
|
||
pandoc.Space(),
|
||
})
|
||
local name_inlines = type(affil.name) == "table" and affil.name or { pandoc.Str(tostring(affil.name)) }
|
||
local city_inlines = type(affil.city) == "table" and affil.city or { pandoc.Str(tostring(affil.city)) }
|
||
local postcode_inlines = type(affil["postal-code"]) == "table" and affil["postal-code"]
|
||
or { pandoc.Str(tostring(affil["postal-code"])) }
|
||
local country_inlines = type(affil.country) == "table" and affil.country
|
||
or { pandoc.Str(tostring(affil.country or affil["postal-code"])) }
|
||
return num_inlines
|
||
:extend(name_inlines)
|
||
:extend({ pandoc.Str(", ") })
|
||
:extend(city_inlines)
|
||
:extend({ pandoc.Space() })
|
||
:extend(postcode_inlines)
|
||
:extend({ pandoc.Str(", ") })
|
||
:extend(country_inlines)
|
||
:extend({ pandoc.Str(".") })
|
||
end)
|
||
local combined_inlines = pandoc.List:new()
|
||
for i, line in ipairs(affil_lines) do
|
||
combined_inlines:extend(line)
|
||
if i < #affil_lines then
|
||
combined_inlines:extend({ pandoc.LineBreak() })
|
||
end
|
||
end
|
||
return { pandoc.Para(combined_inlines) }
|
||
end
|
||
|
||
M.create_affiliations_blocks = create_affiliations_blocks_alphabeta
|
||
|
||
function Meta(meta)
|
||
local affilstyle = meta and meta.affilstyle and stringify(meta.affilstyle) or "alphabeta"
|
||
M.create_affiliations_blocks = affilstyle == "number" and create_affiliations_blocks_number
|
||
or create_affiliations_blocks_alphabeta
|
||
if meta.authors then
|
||
meta.author = create_authors_inlines(meta.authors, M.default_marks, meta)
|
||
end
|
||
if meta.affiliations then
|
||
meta.institute = M.create_affiliations_blocks(meta.affiliations, meta)
|
||
end
|
||
if meta.authors then
|
||
local equal_contributors = create_equal_contributors_block(meta.authors, M.default_marks)
|
||
if equal_contributors then
|
||
meta["equal-contributors"] = equal_contributors
|
||
end
|
||
local correspondence = create_correspondence_blocks(meta.authors, M.default_marks)
|
||
if correspondence then
|
||
meta.correspondence = correspondence
|
||
end
|
||
end
|
||
return meta
|
||
end
|
||
|
||
return M
|