Module:Taxobox

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: -- AddCaption: -- AddCategory: 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", category = "IUCN Red List endangered species" }, VU = { file = "VU", label = "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", category = "IUCN Red List critically endangered species" }, EN = { file = "EN", label = "Endangered", category = "IUCN Red List endangered species" }, VU = { file = "VU", label = "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", 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 "† " .. title == g or "†" .. title == g then passing.above = "''" .. g .. "''"			-- TODO: add 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 .. " (" .. 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 .. " (" .. 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] = "\n" .. args.subdivision .. " "		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] = "Synonyms" .. ( args.synonyms_ref or "") passing["data" .. i + 1] = "\n" .. args.synonyms .. " "	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