///|
fn name_initial(given : String, marker : String) -> String {
let out = []
for word in split_ws(given) {
let parts = word.split("-").to_array()
let initials = []
for i = 0; i < parts.length(); i = i + 1 {
if parts[i].to_owned().to_array().get(0) is Some(c) {
let initial = c.to_string() + marker
initials.push(
if i + 1 < parts.length() {
initial.trim().to_owned()
} else {
initial
},
)
}
}
out.push(join_nonempty(initials, "-"))
}
join_nonempty(out, "").trim().to_owned()
}
///|
fn format_name(name : CslName, node : NameNode, index : Int32) -> String {
if !name.literal.is_empty() {
return name.literal
}
let given = if node.initialize {
name_initial(name.given, node.initialize_with)
} else {
name.given
}
let family = join_nonempty([name.non_dropping_particle, name.family], " ")
if node.form == "short" {
return family
}
let given = join_nonempty([given, name.dropping_particle], " ")
let inverted = node.name_as_sort_order == "all" ||
(node.name_as_sort_order == "first" && index == 0)
let separator = node.options.get("sort-separator").unwrap_or(", ")
let value = if inverted {
join_nonempty([family, given], separator)
} else {
join_nonempty([given, family], " ")
}
if name.suffix.is_empty() {
value
} else {
value + ", " + name.suffix
}
}
///|
fn format_names(
names : Array[CslName],
node : NameNode,
style : Style,
format : String,
) -> String {
if names.is_empty() {
return ""
}
if node.form == "count" {
return names.length().to_string()
}
let mut limit = names.length()
if node.et_al_min > 0 && names.length() >= node.et_al_min {
limit = node.et_al_use_first.max(1).min(names.length())
}
let shown = []
for i = 0; i < limit; i = i + 1 {
shown.push(
apply_affix(
node.name_affix,
output_escape(format_name(names[i], node, i), format),
format~,
),
)
}
let delimiter = output_escape(node.delimiter, format)
let mut result = join_nonempty(shown, delimiter)
if limit < names.length() {
let precedes = node.options
.get("delimiter-precedes-et-al")
.unwrap_or("contextual")
let et_al_separator = if precedes == "always" ||
(precedes == "contextual" && limit > 1) ||
(
precedes == "after-inverted-name" &&
(
node.name_as_sort_order == "all" ||
(node.name_as_sort_order == "first" && limit == 1)
)
) {
delimiter
} else {
" "
}
if node.options.get("et-al-use-last").unwrap_or("false") == "true" &&
limit < names.length() - 1 {
result = result +
et_al_separator +
"… " +
output_escape(
format_name(names[names.length() - 1], node, names.length() - 1),
format,
)
} else {
result = result +
et_al_separator +
output_escape(style.locale.term("et-al"), format)
}
} else if !node.and_word.is_empty() && shown.length() > 1 {
let last = shown.pop().unwrap()
let precedes = node.delimiter_precedes_last
let inverted = node.name_as_sort_order == "all" ||
(node.name_as_sort_order == "first" && shown.length() == 1)
let sep = if precedes == "always" ||
(precedes == "contextual" && shown.length() > 1) ||
(precedes == "after-inverted-name" && inverted) {
delimiter
} else {
" "
}
let conjunction = if node.and_word == "symbol" {
"&"
} else {
style.locale.term("and")
}
result = join_nonempty(shown, delimiter) +
sep +
output_escape(conjunction, format) +
" " +
last
}
if node.label is Some(label) {
let plural = names.length() > 1
let term = if node.label_variable == "editor" {
if label.attr("form") == "short" {
if plural {
"eds."
} else {
"ed."
}
} else if plural {
"editors"
} else {
"editor"
}
} else {
node.label_variable
}
result = result +
apply_affix(read_affix(label), output_escape(term, format), format~)
}
result
}