local p = {}
local infobox = require("Module:Infobox")
local infoboxImage = require("Module:InfoboxImage")
local arguments = require("Module:Arguments")
local ifPreview = require("Module:If preview")
local colors = {
animalia = "ebebd2",
archaea = "c3f5fa",
archaeplastida = "b4fab4",
bacteria = "dcebf5",
eukaryota = "f5d7ff",
fungi = "91fafa",
incertae_sedis = "faf0e6",
sar = "c8fa50",
veterovata = "fafadc",
virus = "fafabe",
}
-- aliases that use the same color
-- TODO: combine the tables?
local colorAliases = {
animal = "animalia",
nanoarchaeota = "archaea",
nanarchaeota = "archaea",
korarchaeota = "archaea",
thaumarchaeota = "archaea",
crenarchaeota = "archaea",
euryarchaeota = "archaea",
plantae = "archaeplastida",
plant = "archaeplastida",
viridiplantae = "archaeplastida",
firmicutes = "bacteria",
eubacteria = "bacteria",
eukaryote = "eukaryota",
eukarya = "eukaryota",
excavata = "eukaryota",
excavates = "eukaryota",
protista = "eukaryota",
protists = "eukaryota",
amoebozoa = "eukaryota",
opisthokonta = "eukaryota",
choanozoa = "eukaryota",
acritarcha = "incertae_sedis",
chromalveolata = "sar",
viroid = "virus",
viroids = "virus",
viruses = "virus",
i = "virus",
ii = "virus",
iii = "virus",
iv = "virus",
v = "virus",
vi = "virus",
vii = "virus",
vii = "virus",
}
-- This table shows which order the color search should go
local taxonOrder = {
"phylum", "unranked_phylum", "divisio", "unranked_superdivisio", "superphylum", "unranked_superphylum", "superdivision", "unranked_superdivisio",
"subregnum", "unranked_subregnum", "regnum", "unranked_regnum", "superregnum", "unranked_superregnum", "domain", "unranked_domain", "virus_group"
}
-- Gets the background color as a hex code, if possible
--
-- @param args the arguments passed in to the template
--
-- @return a hex color, or nil if no color should be shown
local function getColor(args)
for _, taxon in ipairs(taxonOrder) do
local v = args[taxon]
if v then
local delinked = mw.ustring.gsub(mw.ustring.lower(v), "[%]%[']", "") -- delink, converting "[[''name'']]" to "name"
local i = mw.ustring.find(delinked, "|")
local sanitized = i and mw.ustring.sub(delinked, 0, i - 1) or delinked -- get "abx" from "abc|xyz"
local hex = colors[sanitized] or colors[colorAliases[sanitized]]
if hex then
return hex
end
end
end
end
local classification = {
{ arg_name = "superdomain", label = "Superdomain" },
{ arg_name = "domain", label = "Domain" },
{ arg_name = "superregnum", label = "Superkingdom" },
{ arg_name = "regnum", label = "Kingdom" },
{ arg_name = "subregnum", label = "Subkingdom" },
{ arg_name = "superdivisio", label = "Superdivision" },
{ arg_name = "superphylum", label = "Superphylum" },
{ arg_name = "divisio", label = "Division" },
{ arg_name = "phylum", label = "Phylum" },
{ arg_name = "subdivisio", label = "Subdivision" },
{ arg_name = "subphylum", label = "Subphylum" },
{ arg_name = "infraphylum", label = "Infraphylum" },
{ arg_name = "microphylum", label = "Microphylum" },
{ arg_name = "nanophylum", label = "Nanophylum" },
{ arg_name = "superclassis", label = "Superclass" },
{ arg_name = "classis", label = "Class" },
{ arg_name = "subclassis", label = "Subclass" },
{ arg_name = "infraclassis", "Infraclass" },
{ arg_name = "magnordo", label = "Magnorder" },
{ arg_name = "superordo", label = "Superorder" },
{ arg_name = "ordo", label = "Order" },
{ arg_name = "subordo", label = "Suborder" },
{ arg_name = "infraordo", label = "Infraorder" },
{ arg_name = "parvordo", label = "Parvorder" },
{ arg_name = "zoodivisio", label = "Division" },
{ arg_name = "zoosectio", label = "Section" },
{ arg_name = "zoosubsectio", label = "Subsection" },
{ arg_name = "superfamilia", label = "Superfamily" },
{ arg_name = "familia", label = "Family" },
{ arg_name = "subfamilia", label = "Subfamily" },
{ arg_name = "supertribus", label = "Supertribe" },
{ arg_name = "tribus", label = "Tribe" },
{ arg_name = "subtribus", label = "Subtribe" },
{ arg_name = "alliance", label = "''Alliance''" },
{ arg_name = "genus", label = "Genus" },
{ arg_name = "subgenus", label = "Subgenus" },
{ arg_name = "sectio", label = "Section" },
{ arg_name = "subsectio", label = "Subsection" },
{ arg_name = "series", label = "Series" },
{ arg_name = "subseries", label = "Subseries" },
{ arg_name = "species_group", label = "''Species group''" },
{ arg_name = "species_subgroup", label = "''Species subgroup''" },
{ arg_name = "species_complex", label = "''Species complex''" },
{ arg_name = "species", label = "Species" },
{ arg_name = "subspecies", label = "Subspecies" },
{ arg_name = "variety", label = "Variety" }, -- no unranked
{ arg_name = "varias", label = "Variety" }, -- no unranked; alias of variety
{ arg_name = "forma", label = "Form" }, -- no unranked
}
-- InfoboxImage: [[File:Status <status_system> <file>.svg|link=|alt=<category>]]
-- AddCaption: <label>
-- AddCategory: [[Category:<category>]]
local statusSystems = {
["iucn2.3"] = {
link = "IUCN Red List",
EX = { file = "EX", label = "[[Extinct]]", category = "IUCN Red List extinct species" },
EW = { file = "EW", label = "[[Extinct in the wild]]", category = "IUCN Red List extinct in the wild species" },
CR = { file = "CR", label = "[[Critically endangered]]", category = "IUCN Red List critically endangered species" },
EN = { file = "EN", label = "[[Endangered species|Endangered]]", category = "IUCN Red List endangered species" },
VU = { file = "VU", label = "[[Vulnerable species|Vulnerable]]", category = "IUCN Red List vulnerable species" },
LR = {file = "blank", label = "Lower risk", category = "Invalid conservation status" },
CD = { file = "CD", label = "[[Conservation dependent]]", category = "IUCN Red List conservation dependent species" },
["LR/CD"] = { file = "CD", label = "[[Conservation dependent]]", category = "IUCN Red List conservation dependent species" }, -- duplicate
NT = { file = "NT", label = "[[Near threatened]]", category = "IUCN Red List near threatened species" },
["LR/NT"] = { file = "NT", label = "[[Near threatened]]", category = "IUCN Red List near threatened species" }, -- duplicate
LC = { file = "LC", label = "[[Least concern]]", category = "IUCN Red List near threatened species" },
["LR/LC"] = { file = "LC", label = "[[Least concern]]", category = "IUCN Red List near threatened species" },
DD = { file = "blank", label = "[[Least deficient]]", category = "IUCN Red List data deficient species" },
NE = { label = "''Not evaluated''" },
NR = { label = "''Not recognized''" },
PE = { file = "CR", label = "[[Critically endangered]], possibly extinct", category = "IUCN Red List critically endangered species" },
PEW = { file = "CR", label = "[[Critically endangered]], possibly extinct in the wild", category = "IUCN Red List critically endangered species" },
},
["iucn3.1"] = {
link = "IUCN Red List",
EX = { file = "EX", label = "[[Extinct]]", category = "IUCN Red List extinct species" },
EW = { file = "EW", label = "[[Extinct in the wild]]", category = "IUCN Red List extinct in the wild species" },
CR = { file = "CR", label = "[[Critically endangered species|Critically endangered]]", category = "IUCN Red List critically endangered species" },
EN = { file = "EN", label = "[[Endangered species (IUCN status)|Endangered]]", category = "IUCN Red List endangered species" },
VU = { file = "VU", label = "[[Vulnerable species|Vulnerable]]", category = "IUCN Red List vulnerable species" },
NT = { file = "NT", label = "[[Near threatened]]", category = "IUCN Red List near threatened species" },
LC = { file = "LC", label = "[[Least concern]]", category = "IUCN Red List least concern species" },
DD = { file = "blank", label = "[[Data deficient]]", category = "IUCN Red List data deficient species" },
NE = { label = "''Not evaluated''" },
NR = { label = "''Not recognized''" },
PE = { file = "CR", label = "[[Critically endangered]]", category = "IUCN Red List critically endangered species" },
PEW = { file = "CR", label = "[[Critically endangered]]", category = "IUCN Red List critically endangered species" },
},
["epbc"] = {
link = "EPBC Act",
EX = { file = "EX", label = "[[Extinct]]", category = "EPBC Act extinct biota" },
EW = { file = "EW", label = "[[Extinct in the wild]]", category = "EPBC Act extinct in the wild biota" },
CR = { file = "CR", label = "[[Critically endangered]]", category = "EPBC Act critically endangered biota" },
EN = { file = "EN", label = "[[Endangered]]", category = "EPBC Act endangered biota" },
VU = { file = "VU", label = "[[Vulnerable species|Vulnerable]]", category = "EPBC Act vulnerable biota" },
CD = { file = "CD", label = "[[Conservation dependent]]", category = "EPBC Act conservation dependent biota" },
DL = { file = "DL", label = "Delisted" },
},
}
local function getFirst(e, p)
return e and e[p] and e[p][1]
end
function p.main(frame)
local args = arguments.getArgs(frame)
return p._main(args, frame)
end
-- Helper function to dynamically retrieve the taxon "authority"
-- and present it in paretheses
--
-- @param args the table of arguments passed into the template
-- @param taxon the taxon to check
--
-- @return nil or text to serve as the taxon name and its authority
local function authorityHelp(args, taxon)
if args[taxon] then
local authority = args[taxon .. "_authority"]
return args[taxon] .. (authority and " (" .. authority .. ")" or "") -- when authority is nil no parentheses appear
end
end
-- uses the qualifiers table provided to construct an authority string
function p._authorityHelp(qualifiers)
local p405 = getFirst(qualifiers, "P405")
local authority = p405 and p405["datavalue"] and p405["datavalue"]["value"] and p405["datavalue"]["value"]["id"]
local q = authority and mw.wikibase.getEntity(authority)
local lastNameP = q and q["claims"] and getFirst(q["claims"], "P835")
local lastName = lastNameP and lastNameP["mainsnak"] and lastNameP["mainsnak"]["datavalue"] and lastNameP["mainsnak"]["datavalue"]["value"]
local p574 = getFirst(qualifiers, "P574")
local year = p574 and p574["datavalue"] and p574["datavalue"]["value"] and p574["datavalue"]["value"]["time"]
if lastName and year then return " (" .. lastName .. ", " .. year .. ")"
elseif lastName or year then return " (" .. (lastName or year) .. ")"
else return ""
end
end
function p._main(args, frame)
local warnings = {} -- warnings to display on preview
local passing = {} -- arguments passed to Module:Infobox
-- Safely replace spaces in parameters with underscores, removing the original parameter and sending a warning
for k, _ in pairs(args) do
if mw.ustring.find(k, " ") then
local k1 = mw.ustring.gsub(s, " ", "_")
if not args[k1] then
table.insert(warnings, "deprecated parameter \"" .. k .. "\". Please use \"" .. k1 .. "\" instead.")
args[k1] = args[k] -- copy value
args[k] = nil -- empty
else
table.insert(warnings, "found \"" .. k .. "\" and \"" .. k1 .. "\". Using \"" .. k1 .. "\" instead.")
end
end
end
if args.name then
passing.above = args.name
elseif args.genus or args.species or args.binomial then
local title = mw.title.getCurrentTitle().baseText
local g = mw.ustring.gsub(args.genus or args.species or args.binomial, "'", "")
if title == g or "<abbr title=\"Extinct\" aria-label=\"Extinct\" style=\"border: none; text-decoration: none; cursor: inherit; font-weight: normal; font-style: normal;\">†</abbr>" .. title == g or "†" .. title == g then
passing.above = "''" .. g .. "''"
-- TODO: add {{Italic title}}
else
passing.above = title
end
else
passing.above = mw.title.getCurrentTitle().baseText
end
if args.fossil_range then
passing.subheader = "Temporal range: " .. args.fossil_range
end
-- apply header styles
local hex = args.color_as or args.colour_as or getColor(args)
if hex then
local style = "background-color: #" .. hex .. ";"
passing.abovestyle = style
passing.subheaderstyle = style
passing.headerstyle = style
end
if args.image then
passing.image = infoboxImage.InfoboxImage({args = {image = args.image, upright = args.upright or args.image_upright, alt = args.alt or args.image_alt}})
passing.caption = args.caption or args.image_caption
end
if args.image2 then
passing.image2 = infoboxImage.InfoboxImage({args = {image = args.image2, upright = args.upright2 or args.image2_upright, alt = args.alt2 or args.image2_alt}})
passing.caption2 = args.caption2 or args.image2_caption
end
local i = 1
-- Embed photos into the infobox via frame:expandTemplate
-- Trying to call Module:Infobox keeps the previous state
if args.status or args.status2 then
local subbox = {
child = "yes",
}
if args.status and args.status_system then
args.status = mw.ustring.upper(args.status)
if args.status_system == "IUCN2.3" or args.status_system == "IUCN3.1" then
args.status_system = mw.ustring.lower(args.status_system)
end
if statusSystems[args.status_system] and statusSystems[args.status_system][args.status] then
local x = statusSystems[args.status_system][args.status]
subbox.image1 = infoboxImage.InfoboxImage({args = { image = "Status " .. args.status_system .. " " .. args.status .. ".svg", alt = (x.category or nil)}})
subbox.caption1 = x.label .. " ([[" .. statusSystems[args.status_system].link .. "|" .. args.status_system .. "]])" .. (args.status_ref or "")
end
end
if args.status2 and args.status2_system then
args.status2 = mw.ustring.upper(args.status2)
if args.status2_system == "IUCN2.3" or args.status2_system == "IUCN3.1" then
args.status2_system = mw.ustring.lower(args.status2_system)
end
if statusSystems[args.status2_system] and statusSystems[args.status2_system][args.status2] then
local x = statusSystems[args.status2_system][args.status2]
subbox.image2 = infoboxImage.InfoboxImage({args = { image = "Status " .. args.status2_system .. " " .. args.status .. ".svg", alt = (x.category or nil)}})
subbox.caption2 = x.label .. " ([[" .. statusSystems[args.status2_system].link .. "|" .. args.status2_system .. "]])" .. (args.status2_ref or "")
end
end
passing["header" .. i] = "[[Conservation status]]"
passing["data" .. i + 1] = frame:expandTemplate({title = "infobox", args = subbox})
i = i + 2
end
if args.virus or args.virus_group then
passing["header" .. i] = "[[Virus classification]]"
elseif args.ichnos then
passing["header" .. i] = "[[Trace fossil classification]]"
elseif args.veterovata then
passing["header" .. i] = "[[Egg fossil classification]]"
else
passing["header" .. i] = "[[Scientific classification]]"
end
i = i + 1
if args.virus_group then
local g = {
i = "Group I ([[dsDNA]])",
ii = "Group II ([[ssDNA]])",
iii = "Group III ([[dsRDNA]])",
iv = "Group IV ([[(+)ssRNA]])",
v = "Group V ([[(-)ssRNA]])",
vi = "Group VI ([[ssRNA-RT]])",
["vi/vii"] = "Groups VI and VII",
vii = "Group VII ([[dsDNA-RT]])",
}
passing["label" .. i] = "Virus group"
passing["data" .. i] = g[args.virus_group] or args.virus_group
i = i + 1
end
local q = args['wikidata_item'] or args['qid'];
local entity = mw.wikibase.getEntity(q)
if args.superdomain or args.domain or args.superregnum or args.regnum or args.subregnum or args.superdivisio
or args.superphylum or args.divisio or args.phylum or args.subdivisio or args.subphylum or args.infraphylum
or args.microphylum or args.nanophylum or args.superclassis or args.classis or args.subclassis or args.infraclassis
or args.magnordo or args.superordo or args.ordo or args.subordo or args.infraordo or args.parvordo or args.zoodivisio
or args.zoosectio or args.zoosubsectio or args.superfamilia or args.familia or args.subfamilia or args.supertribus
or args.tribus or args.subtribus or args.alliance or args.genus or args.subgenus or args.sectio or args.subsectio
or args.series or args.subseries or args.species_group or args.species_subgroup or args.species_complex
or args.species or args.subspecies or args.variety or args.varias or args.forma then
for _, t in ipairs(classification) do
local k = t.arg_name
local v = t.label
if args["unranked_" .. k] then
passing["label" .. i] = "(unranked)"
passing["data" .. i] = authorityHelp(args, "unranked_" .. k)
i = i + 1
end
if args[k] then
passing["label" .. i] = v
passing["data" .. i] = authorityHelp(args, k)
i = i + 1
end
end
elseif entity then
local p171 = getFirst(entity["claims"], "P171")
if p171 and p171["mainsnak"] and p171["mainsnak"]["datavalue"] and p171["mainsnak"]["datavalue"]["value"] and p171["mainsnak"]["datavalue"]["value"]["id"] then
local parent = mw.wikibase.getEntity(p171["mainsnak"]["datavalue"]["value"]["id"])
if parent then
local p105 = getFirst(parent["claims"], "P105")
passing["label" .. i] = p105 and p105["mainsnak"] and p105["mainsnak"]["datavalue"] and p105["mainsnak"]["datavalue"]["value"] and p105["mainsnak"]["datavalue"]["value"]["id"] or nil
local p225 = getFirst(parent["claims"], "P225")
passing["data" .. i] = p225 and p225["mainsnak"] and p225["mainsnak"]["datavalue"] and p225["mainsnak"]["datavalue"]["value"] or nil
i = i + 2
end
end
end
if args.virus_infrasp and not args.virus_infrasp_rank then
table.insert(warnings, "\"virus_infrasp_rank\" missing")
elseif args.virus_infrasp then
passing["label" .. i] = args.virus_infrasp_rank
end
if args.binomial then
passing["header" .. i] = "[[Binomial name]]"
passing["data" .. i + 1] = authorityHelp(args, "binomial")
i = i + 2
elseif entity then
local binomial = getFirst(entity["claims"], "P225")
if binomial and binomial["mainsnak"] and binomial["mainsnak"]["datavalue"] and binomial["mainsnak"]["datavalue"]["value"] then
passing["header" .. i] = "[[Binomial name]]"
passing["data" .. i + 1] = binomial["mainsnak"]["datavalue"]["value"] .. p._authorityHelp(binomial["qualifiers"])
end
end
if args.trinomial then
passing["header" .. i] = "[[Trinomial name]]"
passing["data" .. i + 1] = authorityHelp(args, "trinomial")
i = i + 2
end
if args.range_map then
passing["data" .. i] = frame:expandTemplate({title = "Infobox", args = { child = "yes", image = infoboxImage.InfoboxImage({args = {image = args.range_map, alt = args.range_map_alt, upright = args.range_map_upright}}), caption = args.range_map_caption}})
i = i + 1
end
if args.range_map2 then
passing["data" .. i] = frame:expandTemplate({title = "Infobox", args = { child = "yes", image = infoboxImage.InfoboxImage({args = {image = args.range_map2, alt = args.range_map2_alt, upright = args.range_map2_upright}}), caption = args.range_map2_caption}})
i = i + 1
end
if args.range_map3 then
passing["data" .. i] = frame:expandTemplate({title = "Infobox", args = { child = "yes", image = infoboxImage.InfoboxImage({args = {image = args.range_map3, alt = args.range_map3_alt, upright = args.range_map3_upright}}), caption = args.range_map3_caption}})
i = i + 1
end
if args.range_map4 then
passing["data" .. i] = frame:expandTemplate({title = "Infobox", args = { child = "yes", image = infoboxImage.InfoboxImage({args = {image = args.range_map4, alt = args.range_map4_alt, upright = args.range_map4_upright}}), caption = args.range_map4_caption}})
i = i + 1
end
if args.type_genus then
passing["header" .. i] = "[[Type ichnogenus]]"
passing["data" .. i + 1] = authorityHelp(args, "type_genus")
i = i + 2
end
if args.type_oogenus then
passing["header" .. i] = "[[Type oogenus]]"
passing["data" .. i + 1] = authorityHelp(args, "type_oogenus")
i = i + 2
end
if args.type_species then
passing["header" .. i] = "[[Type species]]"
passing["data" .. i + 1] = authorityHelp(args, "type_species")
i = i + 2
end
if args.type_oospecies then
passing["header" .. i] = "[[Type oospecies]]"
passing["data" .. i + 1] = authorityHelp(args, "type_oospecies")
i = i + 2
end
if args.type_strain then
passing["header" .. i] = "[[Type strain]]"
passing["data" .. i + 1] = authorityHelp(args, "type_strain") .. (args.type_strain_ref or "")
i = i + 2
end
if args.subdivision then
mw.log(args.subdivision)
passing["header" .. i] = args.subdivision_ranks or "Species" .. (args.subdivision_ref or "")
passing["data" .. i + 1] = "<div style=\"text-align: left;\">\n" .. args.subdivision .. "</div>"
i = i + 2
end
if args.possible_subdivision then
passing["header" .. i] = args.possible_subdivision_ranks or "Possible species" .. (args.possible_subdividion_ref or "")
passing["data" .. i + 1] = args.possible_subdivision
i = i + 2
end
if args.synonyms then
passing["header" .. i] = "[[Synonym (taxonomy)|Synonyms]]" .. ( args.synonyms_ref or "")
passing["data" .. i + 1] = "<div style=\"text-align: left;\">\n" .. args.synonyms .. "</div>"
end
local out = infobox.infobox(passing)
for _, warning in ipairs(warnings) do
local w = ifPreview._warning({warning})
if w and w ~= '' then
out = out .. w
end
end
return out
end
return p