local export = {}
local tag_text = require("Module:script utilities").tag_text
local get_lang = require("Module:languages").getByCode
local utils = require("Module:bibliography/utils")
-- Understands if a number refers to one or multiple instances of something (e.g. pages, sections) and pluralises the label accordingly.
local function format_number_label(value, label)
if type(label) == "table" then
label = label
end
return label..value
end
-- Handles the set_page, set_id, etc. functions within page_url, entry_url, etc. nodes.
local function apply_param_setter(arg_name, setter, input_data)
local input_value = input_data
-- simple addition or subtraction
if type(setter) == "number" and input_value then
return input_value + setter
-- operations as strings
-- TODO: needs to be stricter with invalid inputs
elseif type(setter) == "string" and input_value then
-- separate the different actions within the setter string (operations, alterations)
local operations = setter:match("^(*)")
local esc_operations = "^"..operations:gsub("()", "%%%1")
local alterations = setter:match(esc_operations.."(>+)")
assert(alterations or not setter:match(esc_operations.."."), "Invalid string as set_page: "..setter)
-- apply arithmetic operations
local function calculate_operation(num, operations)
assert(operations:gsub("() *(%d+)", function(operation, operation_num)
num = ({
= function(a, b) return a + b end,
= function(a, b) return a - b end,
= function(a, b) return a * b end,
= function(a, b) return math.floor(a / b) end,
})(num, tonumber(operation_num))
return ""
end):match("^ *$"), "Invalid operation in set_page: "..setter)
return num
end
input_value = calculate_operation(input_value, operations)
-- apply alterations (e.g. for missing or duplicate pages)
if alterations then
assert(alterations:gsub("> *(%d+) *(*)", function(alteration_from, alteration_operation)
if input_data > tonumber(alteration_from) then
input_value = calculate_operation(input_value, alteration_operation)
end
return ""
end):match("^ *$"), "Invalid alteration in set_page: "..setter)
end
return input_value
-- full function for complex operation
elseif type(setter) == "function" then
return setter(input_data)
end
end
-- Handles page_url, entry_url, etc.
local function param_url(source, const_data, param_name)
if not const_data then return nil end
local input_data = {}
for k, v in pairs(const_data) do
if not source.unprocess_numbers and type(v) == "string"
and v:match("^%d+") and (k == "page" or k == "volume") then
-- TODO: understand if it is allowed to have non-standard parameters into the arguments
-- table which is given to the module. we could set numerical = true inside that table
v = tonumber(v:match("^%d+"))
end
input_data = v
end
local url_config, link_node
if input_data.volume and source.volumes and source.volumes then
local current_volume = source.volumes
if current_volume then
url_config = current_volume
link_node = current_volume.link
end
end
url_config = url_config or source
if not url_config then return nil end
if type(url_config) ~= "table" then
url_config = { url = url_config }
else
-- applies setters
for set_arg, set_value in pairs(url_config) do
if set_arg:match("^set_") then
set_arg = set_arg:gsub("^set_", "")
input_data = apply_param_setter(set_arg, set_value, input_data)
end
end
end
link_node = link_node or source.link
-- percent encodes entry name
if input_data.entry then
input_data.entry = mw.uri.encode(input_data.entry)
end
-- formats the URL
local function resolve_value(site_key)
local value = url_config
-- recycle data from stable links
if value == true then
value = link_node
end
if not value then error("Could not retrieve the "..site_key.." data for "..param_name) end
-- if a function, runs it
if type(value) == "string" then
return value
elseif type(value) == "function" then
return value(input_data)
end
error("Could not define "..param_name.." URL from the site "..site_key)
end
if url_config.ia then
return "https://archive.org/details/"..resolve_value("ia")
.."/page/"..(url_config.pref or "n")
..input_data.page.."/mode/1up?view=theater"
elseif url_config.gb then
return "https://books.google.com/?id="..resolve_value("gb").."&pg="..(url_config.pref or "PA")..input_data.page
elseif url_config.url then
local do_break = false
local ret = resolve_value("url"):gsub("%$(*)%$", function (wanted_param_list)
local param_ret
for wanted_param in wanted_param_list:gmatch("*") do
param_ret = input_data
if param_ret then break end
end
if not param_ret then do_break = true end
return param_ret
end)
if do_break then return end
return ret
end
error("No URL could be made")
end
-- load sources data from submodule
local function get_submodule_data(input_lang, input_sid)
local submodule_data = require("Module:bibliography/data/"..input_lang)
-- find the source in among data
local source, sid = (function ()
local sources = submodule_data.sources
-- simply return the source if easily accessible
if sources then
return sources, input_sid
end
for iter_sid, iter_source in pairs(sources) do
-- maybe if stripped it is the same
if utils.is_abbrev(iter_sid) then
if utils.strip_markup(iter_sid) == input_sid then
return iter_source, iter_sid
end
end
-- try with the aliases
local alias = iter_source.alias
if alias then
if type(alias) == "string" then
if alias == input_sid then
return iter_source, iter_sid
end
elseif type(alias) == "table" then
for _, v in ipairs(alias) do
if v == input_sid then
return iter_source, iter_sid
end
end
end
end
end
error("could not find the source with the ID "..input_sid.." in the database ]")
end)()
-- handle importing
if source.import_from then source, sid, input_lang = get_submodule_data(source.import_from, sid) end
-- deduce the bib_page
local bib_page = submodule_data.bib_page
if not bib_page then
bib_page = get_lang(input_lang, "could not deduce the page to link to from invalid language code", true, true):getCanonicalName()
end
return source, sid, input_lang, "Appendix:Bibliography/"..bib_page
end
function export.show(frame)
-- universal parameters
local list_allow_holes = { list = true, allow_holes = true }
local list_entry_alias = { list = true, alias_of = "entry" }
local list_page_alias = { list = true, alias_of = "page" }
local list_section_alias = { list = true, alias_of = "section" }
local volume_alias = { list = true, alias_of = "volume" }
local params = {
-- language
= true,
-- source id
= true,
-- headword
= list_allow_holes,
= list_entry_alias,
= list_entry_alias,
= list_entry_alias,
= list_entry_alias,
-- page
= list_allow_holes,
= list_page_alias,
= list_page_alias,
-- section
= list_allow_holes,
= list_section_alias,
= list_section_alias,
-- volume
= list_allow_holes,
= volume_alias,
= volume_alias,
-- sub entries
= list_allow_holes,
-- entry ID
= list_allow_holes,
}
local args = frame:getParent().args
local source, sid, tracking_lang, bib_page = get_submodule_data(args, args)
-- source-specific parameters and displays
local own_display = {}
if source.own_args then
for arg_name, arg_data in pairs(source.own_args) do
if arg_data.list and arg_data.allow_holes == nil then
-- lists will be automatically assumed to
arg_data.allow_holes = true
end
if arg_data.display then
local display_after = arg_data.display.after or "entry"
arg_data.display.after = nil
arg_data.display.name = arg_name
if not own_display then own_display = {} end
table.insert(own_display, arg_data.display)
end
params = arg_data
end
end
args = require("Module:parameters").process(frame:getParent().args, params)
args, args = nil, nil
args.pagename = mw.title.getCurrentTitle().text
-- display the source, and handle arguments if different
args.sid_display = utils.format(sid)
args.sid = utils.strip_markup(sid)
if source.default_pagename then
if #args.entry == 0 then
args.entry = args.pagename
args.entry.maxindex = 1
elseif #args.entry == 1 and args.entry == "-" then
args.entry = nil
args.entry.maxindex = 0
end
end
-- apply the set_args parameter
if source.set_args then
local function apply_general_setter(setter)
if type(setter) == "function" then
setter(args)
elseif type(setter) == "string" then
require("Module:bibliography/presets")(args)
elseif type(setter) == "table" then
if #setter > 0 then
for _, setter_child in ipairs(setter) do
apply_general_setter(setter_child)
end
-- TODO: understand whether this is really needed or just complicates syntax
elseif setter.from and setter.to and setter.setter then
for index, from in ipairs(args) do
if not args then
args = setter.setter(from)
elseif args == "-" then
args = nil
end
end
end
end
end
apply_general_setter(source.set_args)
end
local ret = "]"
-- tracks the source being cited
require("Module:debug/track")(utils.tracking_path(tracking_lang, args.sid))
-- handle parameters
local i = 0
local current_volume
while true do i = i + 1
local cit_data = {}
local is_data_over = true
for arg_name, arg in pairs(args) do
if type(arg) == "table" then
if arg then
is_data_over = false
cit_data = arg
end
else
cit_data = arg
end
end
if is_data_over then break end
local is_separator_needed = false
local function format_display_table(arg_data)
local arg_name = arg_data.name
local value = cit_data
if not value then return end
if arg_data.label then
value = format_number_label(value, arg_data.label)
end
if arg_data.url then
local value_url = param_url(source, cit_data, arg_name)
if value_url then value = "" end
end
if arg_data.formatter then
value = arg_data.formatter(value, cit_data, source) or value
end
if is_separator_needed and arg_data.separator then
value = arg_data.separator..value
end
ret = ret..value
is_separator_needed = true
end
local function format_own_display_table_list(own_display_table_list)
if own_display_table_list then
for _, own_display_table in ipairs(own_display_table_list) do
format_display_table(own_display_table)
end
end
end
ret = ret..((cit_data.volume and i > 1) and "; " or ", ")
-- VOLUME
if cit_data.volume then
format_display_table { name = "volume", label = "vol. " }
current_volume = cit_data.volume
ret = ret..", "
elseif current_volume then
cit_data.volume = current_volume
end
format_own_display_table_list(own_display.volume)
-- SECTION
format_display_table {
name = "section",
label = {"§", "§§"},
url = true,
}
format_own_display_table_list(own_display.section)
-- PAGE
format_display_table {
name = "page",
separator = " ",
label = {"page ", "pages "},
url = true,
}
format_own_display_table_list(own_display.page)
-- ENTRY
format_display_table {
name = "entry",
separator = ": ",
url = true,
formatter = function(entry, cit_data, source)
local sub = cit_data.sub
if source.entry_lang then
entry = tag_text(entry, get_lang(source.entry_lang))
if sub then sub = tag_text(sub, get_lang(source.entry_lang)) end
end
if sub then entry = entry.."” → “"..sub end
entry = entry:gsub("()%^(%d+)", "%1<sup>%2</sup>")
return "“"..entry.."”"
end
}
format_own_display_table_list(own_display.entry)
end
return ret
end
return export