Module:Sandbox/Outrowed/Cite book
< Module:Sandbox | Outrowed
local p = {}
---Parse template variadic named parameters specifed by the "prefix"
---* Variadic named parameters is defined as "argN", where "arg" is parameter name, and "N" is a number
---* To specify the parameter name, "prefix" argument is provided
---* It returns a list of "prefix"-matched values, ordered ascending by "N"
---* For example, passing `{ name1: value1, name2: value2, name3: value3 }` produces `{ value1, value2, value3 }`
---@param args table<string, string>
---@param prefix string?
---@return table<number, string>
local function parse_variadic(args, prefix)
local list = {}
local prefix = prefix or ""
if args[prefix] then
list[1] = args[prefix]
end
local size = 0
for key, value in pairs(args) do
local key_match = "^" .. mw.ustring.gsub(prefix, "%-", "%%-") .. "(%d+)"
local index = tonumber(mw.ustring.match(key, key_match))
if index then
list[index] = tostring(value)
size = size + 1
end
end
setmetatable(list, { __size = size })
return list
end
---@alias FullName { first: string; last: string; } contain a person's first and last name
---@param args table<string, string>
---@param prefix string?
---@return table<number, FullName>
local function parse_full_name_list(args, prefix)
local prefix = prefix or ""
local first_names = parse_variadic(args, prefix .. "first")
local last_names = parse_variadic(args, prefix .. "last")
local size = 0
local name_list = {}
for idx, first_name in ipairs(first_names) do
name_list[idx] = {
first = first_name,
last = last_names[idx]
}
size = size + 1
end
setmetatable(name_list, { __size = size })
return name_list
end
---Parse name list table to a list of surname-first formatted strings
---* Surname-first formatted string specifically defined as "<last_name>, <first_name>;"
---@param name_list table<number, FullName>
---@return string
local function parse_surnamefs_str(name_list)
local name_str_list = {}
for _, val in ipairs(name_list) do
table.insert(name_str_list, val.last .. ", " .. val.first)
end
return table.concat(name_str_list, "; ")
end
---Remove dots trail at the end of a string
---@param str string
---@return string
local function remove_dots_trail(str)
return mw.ustring.gsub(str, "[.]+$", "")
end
---Test if a value is nil or "empty"
---* If a value is table, then test if its "empty" by counting its items, otherwise return true
---* If a value is something else, then test if its nil, otherwise return true
---@param value any
---@return boolean
local function is_available(value)
if value == nil then
return false
elseif type(value) == "table" then
if getmetatable(value).__size > 0 then
return true
end
local count = 0
for _, _ in pairs(value) do
count = count + 1
end
return count > 0
else
return true
end
end
---Format an external string, like `[<url> <name>]`
---@param url string
---@param name string
---@return string
local function format_external_link(url, name)
return "[" .. url .. " " .. name .. "]"
end
function p.main(args) -- takes a table as argument
--[[ Book description ]]--
local title = args.title
local date = args.date
local chapter = '"' .. args.chapter .. '"'
local isbn = args.isbn
--[[ Book credits ]]--
local author_list = parse_full_name_list(args)
local editor_list = parse_full_name_list(args, "editor-")
local publisher_list = parse_variadic(args, "publisher")
local author_link_list = parse_variadic(args, "author-link")
--[[ Citing ]]--
local chapter_url = args["chapter-url"]
local url = args.url
local access_date = args["access-date"]
local pages = args.pages
local page = args.page
--[[ Linking ]]--
if chapter_url then
chapter = format_external_link(chapter_url, chapter)
end
if url then
title = format_external_link(url, title)
end
--[[ Output ]]--
-- Citation template according to Wikipedia's {{Cite book}} and [[Help:Citation Style 1]]
-- Title
local output = "''" .. title .. "''" .. "."
-- Prefix date
local creditedDate = ""
if date then
creditedDate = " (" .. date .. ")"
end
-- Prefix
if is_available(author_list) and is_available(editor_list) then
local authors_str = parse_surnamefs_str(author_list)
local editors_str = "In " .. parse_surnamefs_str(editor_list)
if getmetatable(editor_list).__size > 1 then
editors_str = editors_str .. ", (eds.)"
else
editors_str = editors_str .. ", (ed.)"
end
output = authors_str .. creditedDate .. ". " .. editors_str .. ". " .. output
elseif is_available(author_list) and not is_available(editor_list) then
local authors_str = parse_surnamefs_str(author_list)
output = authors_str .. creditedDate .. ". " .. output
elseif is_available(editor_list) and not is_available(author_list) then
local editors_str = parse_surnamefs_str(editor_list)
if chapter then
output = chapter .. ". " .. output
end
if getmetatable(editor_list).__size > 1 then
editors_str = editors_str .. ", eds."
else
editors_str = editors_str .. ", ed."
end
if date then
editors_str = editors_str .. " (" .. date .. ")."
end
output = editors_str .. " " .. output
else
if chapter then
output = chapter .. ". " .. output
end
end
-- Postfix
if is_available(publisher_list) then
output = output .. " "
.. remove_dots_trail(table.concat(publisher_list, ", "))
.. "."
end
local credited = is_available(author_list) or is_available(editor_list)
if date and not credited then
output = output .. " " .. date .. "."
end
if pages then
output = output .. " " .. "pp. " .. pages .. "."
elseif page then
output = output .. " " .. "p. " .. page .. "."
end
if isbn then
output = output .. " " .. "[[w:ISBN|ISBN]] " .. "[[w:Special:BookSources/" .. isbn .. "|" .. isbn .. "]]" .. "."
end
return output
end
function p.call(frame) -- for using {{#invoke}}
local args = frame.args
return p.main(args)
end
function p.template(frame) -- for template page
local args = frame:getParent().args
return p.main(args)
end
return p