Anonymous
×
Create a new article
Write your page title here:
We currently have 15 articles on TwistedFates Database. Type your article name above or click on one of the titles below and start writing!



TwistedFates Database
15Articles

Module:Authority control and Module:Infobox: Difference between pages

(Difference between pages)
m (1 revision imported)
 
wikip>Izno
(add using-infobox tstyles support)
 
Line 1: Line 1:
require('Module:No globals')
local p = {}
local p = {}
local title = mw.title.getCurrentTitle()
local args = {}
local namespace = title.namespace
local origArgs = {}
local testcases = (string.sub(title.subpageText,1,9) == 'testcases')
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'


--[[==========================================================================]]
local function fixChildBoxes(sval, tt)
--[[                            Category functions                            ]]
local function notempty( s ) return s and s:match( '%S' ) end
--[[==========================================================================]]
 
if notempty(sval) then
function p.getCatForId( id )
local marker = '<span class=special_infobox_marker>'
local catName = ''
local s = sval
if namespace == 0 then
s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
catName = 'Wikipedia articles with '..id..' identifiers'
s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
elseif namespace == 2 and not title.isSubpage then
if s:match(marker) then
catName = 'User pages with '..id..' identifiers'
s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
else
s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
catName = 'Miscellaneous pages with '..id..' identifiers'
s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
end
s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
return '[[Category:'..catName..']]'..p.redCatLink(catName)
s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
end
s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
 
s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
function p.redCatLink( catName ) --catName == 'Blah' (not 'Category:Blah', not '[[Category:Blah]]')
s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
if catName and catName ~= '' and
s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
  testcases == false and
end
  mw.title.new(catName, 14).exists == false
if s:match(marker) then
then
local subcells = mw.text.split(s, marker)
return '[[Category:Pages with red-linked authority control categories]]'
s = ''
end
for k = 1, #subcells do
return ''
if k == 1 then
end
s = s .. subcells[k] .. '</' .. tt .. '></tr>'
 
elseif k == #subcells then
function p.createRow( id, label, rawValues, link, links, withUid, specialCat )
local rowstyle = ' style="display:none"'
local catName = 'Wikipedia articles with faulty '..(specialCat or id)..' identifiers'
if notempty(subcells[k]) then rowstyle = '' end
if links then -- all links[] use withUid = false; no check needed
s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
local row = '*<span class="nowrap">'..label
subcells[k]
local totlen = 0
elseif notempty(subcells[k]) then
for i, l in ipairs( links ) do
if (k % 2) == 0 then
if i == 1 then row = row..' '
s = s .. subcells[k]
else          row = row..', ' end
else
if l then
s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
row = row..'<span class="uid">'..l..'</span>'
subcells[k] .. '</' .. tt .. '></tr>'
else
end
row = row..'<span class="error">The '..id..' id '..rawValues[i]..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)
end
end
end
totlen = totlen + #rawValues[i] + 3 -- 3 chars b/w links
end
end
if totlen > 79 then
-- the next two lines add a newline at the end of lists for the PHP parser
row = string.gsub(row, '"nowrap"', '""') -- avoid [[A–Z Series]]
-- [[Special:Diff/849054481]]
end
-- remove when [[:phab:T191516]] is fixed or OBE
return row..'</span>\n'
s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
elseif link then
s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
if withUid then
s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
return '*<span class="nowrap">'..label..' <span class="uid">'..link..'</span></span>\n'
s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
end
return s
return '*<span class="nowrap">'..label..' '..link..'</span>\n'
else
return sval
end
end
return '* <span class="error">The '..id..' id '..rawValues..' is not valid.</span>[[Category:'..catName..']]'..p.redCatLink(catName)..'\n'
end
end


--[[==========================================================================]]
-- Returns the union of the values of two tables, as a sequence.
--[[                      Property formatting functions                      ]]
local function union(t1, t2)
--[[==========================================================================]]
 
-- If a link has a suitable entry in the global inter-wiki prefix table at [[:m:Interwiki_map]], please consider routing through this prefix rather than as external link URL. This will ease future maintenance as necessary updates to the link can be centrally carried out there rather than by updating this module. The "external link" icon would disappear for such entries.


function p.aagLink( id )
local vals = {}
--P3372's format regex: \d+ (e.g. 1)
for k, v in pairs(t1) do
if not id:match( '^%d+$' ) then
vals[v] = true
return false
end
end
return '[https://www.aucklandartgallery.com/explore-art-and-ideas/artist/'..id..'/ '..id..']'..p.getCatForId( 'AAG' )
for k, v in pairs(t2) do
end
vals[v] = true
 
function p.acmLink( id )
--P864's format regex: \d{11} (e.g. 12345678901)
if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d$' ) then
return false
end
end
return '[https://dl.acm.org/profile/'..id..' '..id..']'..p.getCatForId( 'ACM-DL' )
local ret = {}
end
for k, v in pairs(vals) do
 
table.insert(ret, k)
function p.adbLink( id )
--P1907's format regex: [a-z][-a-z]+-([1-2]\d|[1-9])\d{0,3} (e.g. barton-sir-edmund-toby-71)
if not id:match( '^[a-z][-a-z]+-[1-2]%d%d?%d?%d?$' ) and
  not id:match( '^[a-z][-a-z]+-[1-9]%d?%d?%d?$' ) then
return false
end
end
return '[http://adb.anu.edu.au/biography/'..id..' '..id..']'..p.getCatForId( 'ADB' )
return ret
end
end


function p.agsaLink( id )
-- Returns a table containing the numbers of the arguments that exist
--P6804's format regex: [1-9]\d* (e.g. 3625)
-- for the specified prefix. For example, if the prefix was 'data', and
if not id:match( '^[1-9]%d*$' ) then
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
return false
local function getArgNums(prefix)
local nums = {}
for k, v in pairs(args) do
local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
if num then table.insert(nums, tonumber(num)) end
end
end
return '[https://www.agsa.sa.gov.au/collection-publications/collection/creators/_/'..id..'/ '..id..']'..p.getCatForId( 'AGSA' )
table.sort(nums)
return nums
end
end


function p.autoresuyLink( id )
-- Adds a row to the infobox, with either a header cell
--P2558's format regex: [1-9]\d{0,4} (e.g. 12345)
-- or a label/data cell combination.
if not id:match( '^[1-9]%d?%d?%d?%d?$' ) then
local function addRow(rowArgs)
return false
end
if rowArgs.header and rowArgs.header ~= '_BLANK_' then
return '[https://autores.uy/autor/'..id..' '..id..']'..p.getCatForId( 'autores.uy' )
root
end
:tag('tr')
:addClass(rowArgs.rowclass)
:cssText(rowArgs.rowstyle)
:tag('th')
:attr('colspan', '2')
:addClass('infobox-header')
:addClass(rowArgs.class)
:addClass(args.headerclass)
-- @deprecated next; target .infobox-<name> .infobox-header
:cssText(args.headerstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.header, 'th'))
if rowArgs.data then
root:wikitext(
'[[Category:Pages which use infobox templates with ignored data cells]]'
)
end
elseif rowArgs.data and rowArgs.data:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local row = root:tag('tr')
row:addClass(rowArgs.rowclass)
row:cssText(rowArgs.rowstyle)
if rowArgs.label then
row
:tag('th')
:attr('scope', 'row')
:addClass('infobox-label')
-- @deprecated next; target .infobox-<name> .infobox-label
:cssText(args.labelstyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(rowArgs.label)
:done()
end


function p.awrLink( id )
local dataCell = row:tag('td')
--P4186's format regex: (([A-Z]{3}\d{4})|([A-Z]{2}\d{5}))[a-z] (e.g. PR00768b)
dataCell
if not id:match( '^[A-Z][A-Z][A-Z]%d%d%d%d[a-z]$' ) and
:attr('colspan', not rowArgs.label and '2' or nil)
  not id:match( '^[A-Z][A-Z]%d%d%d%d%d[a-z]$' ) then
:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
return false
:addClass(rowArgs.class)
-- @deprecated next; target .infobox-<name> .infobox(-full)-data
:cssText(rowArgs.datastyle)
:cssText(rowArgs.rowcellstyle)
:wikitext(fixChildBoxes(rowArgs.data, 'td'))
else
table.insert(empty_row_categories, rowArgs.data or '')
end
end
return '[http://www.womenaustralia.info/biogs/'..id..'.htm '..id..']'..p.getCatForId( 'AWR' )
end
end


function p.balatLink( id )
local function renderTitle()
--P3293's format regex: \d+ (e.g. 1)
if not args.title then return end
if not id:match( '^%d+$' ) then
return false
end
return '[http://balat.kikirpa.be/object/104257'..id..' '..id..']'..p.getCatForId( 'BALaT' ) --no https as of 9/2019
end


function p.bibsysLink( id )
root
--P1015's format regex: [1-9]\d* or [1-9](\d{0,8}|\d{12}) (e.g. 1234567890123)
:tag('caption')
--TODO: follow up @ [[d:Property talk:P1015#Discrepancy between the 2 regex constraints]] or escalate/investigate
:addClass('infobox-title')
if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) and
:addClass(args.titleclass)
  not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d$' ) then
-- @deprecated next; target .infobox-<name> .infobox-title
return false
:cssText(args.titlestyle)
end
:wikitext(args.title)
return '[https://authority.bibsys.no/authority/rest/authorities/html/'..id..' '..id..']'..p.getCatForId( 'BIBSYS' )
end
end


function p.bildLink( id )
local function renderAboveRow()
--P2092's format regex: \d+ (e.g. 1)
if not args.above then return end
if not id:match( '^%d+$' ) then
return false
end
return '[https://www.bildindex.de/document/obj'..id..' '..id..']'..p.getCatForId( 'Bildindex' )
end


function p.bncLink( id )
root
--P1890's format regex: \d{9} (e.g. 123456789)
:tag('tr')
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
:tag('th')
return false
:attr('colspan', '2')
end
:addClass('infobox-above')
return '[http://www.bncatalogo.cl/F?func=direct&local_base=red10&doc_number='..id..' '..id..']'..p.getCatForId( 'BNC' )
:addClass(args.aboveclass)
-- @deprecated next; target .infobox-<name> .infobox-above
:cssText(args.abovestyle)
:wikitext(fixChildBoxes(args.above,'th'))
end
end


function p.bneLink( id )
local function renderBelowRow()
--P950's format regex: (XX|FF|a)\d{4,7}|(bima|bimo|bica|bis[eo]|bivi|Mise|Mimo|Mima)\d{10} (e.g. XX1234567)
if not args.below then return end
if not id:match( '^[XF][XF]%d%d%d%d%d?%d?%d?$' ) and
  not id:match( '^a%d%d%d%d%d?%d?%d?$' ) and
  not id:match( '^bi[mcsv][aoei]%d%d%d%d%d%d%d%d%d%d$' ) and
  not id:match( '^Mi[sm][eoa]%d%d%d%d%d%d%d%d%d%d$' ) then
return false
end
return '[http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id='..id..' '..id..']'..p.getCatForId( 'BNE' ) --no https as of 9/2019
end


function p.bnfLink( id )
root
--P268's format regex: \d{8}[0-9bcdfghjkmnpqrstvwxz] (e.g. 123456789)
:tag('tr')
if not id:match( '^c?b?%d%d%d%d%d%d%d%d[0-9bcdfghjkmnpqrstvwxz]$' ) then
:tag('td')
return false
:attr('colspan', '2')
end
:addClass('infobox-below')
--Add cb prefix if it has been removed
:addClass(args.belowclass)
if not id:match( '^cb.+$' ) then
-- @deprecated next; target .infobox-<name> .infobox-below
id = 'cb'..id
:cssText(args.belowstyle)
end
:wikitext(fixChildBoxes(args.below,'td'))
return '[https://catalogue.bnf.fr/ark:/12148/'..id..' '..id..'] [https://data.bnf.fr/ark:/12148/'..id..' (data)]'..p.getCatForId( 'BNF' )
end
end


function p.botanistLink( id )
local function addSubheaderRow(subheaderArgs)
--P428's format regex: ('t )?(d')?(de )?(la )?(van (der )?)?(Ma?c)?(De)?(Di)?\p{Lu}?C?['\p{Ll}]*([-'. ]*(van )?(y )?(d[ae][nr]?[- ])?(Ma?c)?[\p{Lu}bht]?C?['\p{Ll}]*)*\.? ?f?\.? (e.g. L.)
if subheaderArgs.data and
--not easily/meaningfully implementable in Lua's regex since "(this)?" is not allowed...
subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
if not mw.ustring.match( id, "^[%u%l%d%. '-]+$" ) then --better than nothing
local row = root:tag('tr')
return false
row:addClass(subheaderArgs.rowclass)
end
local id2 = id:gsub(' +', '%%20')
return '[https://www.ipni.org/ipni/advAuthorSearch.do?find_abbreviation='..id2..' '..id..']'..p.getCatForId( 'Botanist' )
end


function p.bpnLink( id )
local dataCell = row:tag('td')
--P651's format regex: \d{6,8} (e.g. 00123456)
dataCell
if not id:match( '^%d%d%d%d%d%d%d%d$' ) and --original format regex, changed 8/2019 to
:attr('colspan', '2')
  not id:match( '^0?%d%d%d%d%d%d%d$' ) and --allow 1-2 leading 0s, allowed by the website
:addClass('infobox-subheader')
  not id:match( '^0?0?%d%d%d%d%d%d$' ) then
:addClass(subheaderArgs.class)
return false
:cssText(subheaderArgs.datastyle)
:cssText(subheaderArgs.rowcellstyle)
:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
else
table.insert(empty_row_categories, subheaderArgs.data or '')
end
end
return '[http://www.biografischportaal.nl/en/persoon/'..id..' '..id..']'..p.getCatForId( 'BPN' ) --no https as of 9/2019
end
end


function p.canticLink( id )
local function renderSubheaders()
--P1273's format regex: a\d{7}[0-9x] (e.g. a10640745)
if args.subheader then
if not id:match( '^a%d%d%d%d%d%d%d[%dx]$' ) then
args.subheader1 = args.subheader
return false
end
end
return '[http://cantic.bnc.cat/registres/CUCId/'..id..' '..id..']'..p.getCatForId( 'CANTIC' ) --no https as of 10/2019
if args.subheaderrowclass then
end
args.subheaderrowclass1 = args.subheaderrowclass
 
function p.ciniiLink( id )
--P271's format regex: DA\d{7}[\dX] (e.g. DA12345678)
if not id:match( '^DA%d%d%d%d%d%d%d[%dX]$' ) then
return false
end
end
return '[https://ci.nii.ac.jp/author/'..id..'?l=en '..id..']'..p.getCatForId( 'CINII' )
local subheadernums = getArgNums('subheader')
end
for k, num in ipairs(subheadernums) do
 
addSubheaderRow({
function p.cwgcLink( id )
data = args['subheader' .. tostring(num)],
--P1908's format regex: [1-9]\d* (e.g. 75228351)
-- @deprecated next; target .infobox-<name> .infobox-subheader
if not id:match( '^[1-9]%d*$' ) then
datastyle = args.subheaderstyle,
return false
rowcellstyle = args['subheaderstyle' .. tostring(num)],
class = args.subheaderclass,
rowclass = args['subheaderrowclass' .. tostring(num)]
})
end
end
return '[https://www.cwgc.org/find-war-dead/casualty/'..id..'/ '..id..']'..p.getCatForId( 'CWGC' )
end
end


function p.daaoLink( id )
local function addImageRow(imageArgs)
--P1707's format regex: [a-z\-]+\d* (e.g. rolf-harris)
if not id:match( '^[a-z%-]+%d*$' ) then
return false
end
return '[https://www.daao.org.au/bio/'..id..' '..id..']'..p.getCatForId( 'DAAO' )
end


function p.dblpLink( id )
if imageArgs.data and
--P2456's format regex: \d{2,3} /\d+(-\d+)?|[a-z] /[a-zA-Z][0-9A-Za-z]*(-\d+)? (e.g. 123/123)
imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
if not id:match( '^%d%d%d?/%d+$' ) and
  not id:match( '^%d%d%d?/%d+%-%d+$' ) and
local row = root:tag('tr')
  not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*$' ) and
row:addClass(imageArgs.rowclass)
  not id:match( '^[a-z]/[a-zA-Z][0-9A-Za-z]*%-%d+$' ) then
return false
end
return '[https://dblp.org/pid/'..id..' '..id..']'..p.getCatForId( 'DBLP' )
end


function p.dibLink( id )
local dataCell = row:tag('td')
--P6829's format regex: a\d{4}\d?(-[A-D])? (e.g. a1953)
dataCell
if not id:match( '^a%d%d%d%d%d?%-?[A-D]?$' ) then
:attr('colspan', '2')
return false
:addClass('infobox-image')
:addClass(imageArgs.class)
:cssText(imageArgs.datastyle)
:wikitext(fixChildBoxes(imageArgs.data, 'td'))
else
table.insert(empty_row_categories, imageArgs.data or '')
end
end
return '[https://dib.cambridge.org/viewReadPage.do?articleId='..id..' '..id..']'..p.getCatForId( 'DIB' )
end
end


function p.dsiLink( id )
local function renderImages()
--P2349's format regex: [1-9]\d* (e.g. 1538)
if args.image then
if not id:match( '^[1-9]%d*$' ) then
args.image1 = args.image
return false
end
end
return '[http://www.uni-stuttgart.de/hi/gnt/dsi2/index.php?table_name=dsi&function=details&where_field=id&where_value='..id..' '..id..']'..p.getCatForId( 'DSI' )
if args.caption then
end
args.caption1 = args.caption
 
function p.fnzaLink( id )
--P6792's format regex: [1-9]\d* (e.g. 9785)
if not id:match( '^[1-9]%d*$' ) then
return false
end
end
return '[https://findnzartists.org.nz/artist/'..id..'/ '..id..']'..p.getCatForId( 'FNZA' )
local imagenums = getArgNums('image')
end
for k, num in ipairs(imagenums) do
 
local caption = args['caption' .. tostring(num)]
function p.gndLink( id )
local data = mw.html.create():wikitext(args['image' .. tostring(num)])
--P227's format regex: 1[012]?\d{7}[0-9X]|[47]\d{6}-\d|[1-9]\d{0,7}-[0-9X]|3\d{7}[0-9X] (e.g. 4079154-3)
if caption then
if not id:match( '^1[012]?%d%d%d%d%d%d%d[0-9X]$' ) and
data
  not id:match( '^[47]%d%d%d%d%d%d%-%d$' ) and
:tag('div')
  not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%-[0-9X]$' ) and
:addClass('infobox-caption')
  not id:match( '^3%d%d%d%d%d%d%d[0-9X]$' ) then
-- @deprecated next; target .infobox-<name> .infobox-caption
return false
:cssText(args.captionstyle)
:wikitext(caption)
end
addImageRow({
data = tostring(data),
-- @deprecated next; target .infobox-<name> .infobox-image
datastyle = args.imagestyle,
class = args.imageclass,
rowclass = args['imagerowclass' .. tostring(num)]
})
end
end
return '[https://d-nb.info/gnd/'..id..' '..id..']'..p.getCatForId( 'GND' )
end
end


function p.hdsLink( id )
-- When autoheaders are turned on, preprocesses the rows
--P902's format regex: \d{6} (e.g. 050123)
local function preprocessRows()
if not id:match( '^%d%d%d%d%d%d$' ) then
if not args.autoheaders then return end
return false
local rownums = union(getArgNums('header'), getArgNums('data'))
table.sort(rownums)
local lastheader
for k, num in ipairs(rownums) do
if args['header' .. tostring(num)] then
if lastheader then
args['header' .. tostring(lastheader)] = nil
end
lastheader = num
elseif args['data' .. tostring(num)] and
args['data' .. tostring(num)]:gsub(
category_in_empty_row_pattern, ''
):match('^%S') then
local data = args['data' .. tostring(num)]
if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
lastheader = nil
end
end
end
end
return '[https://hls-dhs-dss.ch/fr/articles/'..id..' '..id..']'..p.getCatForId( 'HDS' )
if lastheader then
end
args['header' .. tostring(lastheader)] = nil
 
function p.iaafLink( id )
--P1146's format regex: [0-9][0-9]* (e.g. 012)
if not id:match( '^%d+$' ) then
return false
end
end
return '[https://www.iaaf.org/athletes/_/'..id..' '..id..']'..p.getCatForId( 'IAAF' )
end
end


function p.iciaLink( id )
-- Gets the union of the header and data argument numbers,
--P1736's format regex: \d+ (e.g. 1)
-- and renders them all in order
if not id:match( '^%d+$' ) then
local function renderRows()
return false
end
return '[https://www.imj.org.il/artcenter/newsite/en/?artist='..id..' '..id..']'..p.getCatForId( 'ICIA' )
end


function p.ieuLink( id )
local rownums = union(getArgNums('header'), getArgNums('data'))
--P9070's format regex: [A-Z]\\[A-Z]\\[A-Za-z0-9]+ (e.g. K\Y\Kyiv)
table.sort(rownums)
if not id:match( '^[A-Z]\\[A-Z]\\%w+$' ) then
for k, num in ipairs(rownums) do
return false
addRow({
header = args['header' .. tostring(num)],
label = args['label' .. tostring(num)],
data = args['data' .. tostring(num)],
datastyle = args.datastyle,
class = args['class' .. tostring(num)],
rowclass = args['rowclass' .. tostring(num)],
-- @deprecated next; target .infobox-<name> rowclass
rowstyle = args['rowstyle' .. tostring(num)],
rowcellstyle = args['rowcellstyle' .. tostring(num)]
})
end
end
return '[http://www.encyclopediaofukraine.com/display.asp?linkpath=pages\\'..id..' '..id..']'..p.getCatForId( 'IEU' )
end
end


function p.isniLink( id )
local function renderNavBar()
id = p.validateIsni( id ) --e.g. 0000-0000-6653-4145
if not args.name then return end
if not id then
return false
end
return '[https://isni.org/isni/'..id..' '..id:sub( 1, 4 )..' '..id:sub( 5, 8 )..' '..id:sub( 9, 12 )..' '..id:sub( 13, 16 )..']'..p.getCatForId( 'ISNI' ) --no https as of 9/2019
end


function p.jocondeLink( id )
root
--P347's format regex: [\-0-9A-Za-z]{11} (e.g. 12345678901)
:tag('tr')
local regex = '^'..string.rep('[%-0-9A-Za-z]', 11)..'$'
:tag('td')
if not id:match( regex ) then
:attr('colspan', '2')
return false
:addClass('infobox-navbar')
end
:wikitext(require('Module:Navbar')._navbar{
return '[https://www.pop.culture.gouv.fr/notice/joconde/'..id..' '..id..']'..p.getCatForId( 'Joconde' )
args.name,
mini = 1,
})
end
end


function p.kulturnavLink( id )
local function renderItalicTitle()
--P1248's format regex: [0-9a-f]{8}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{4}\-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
return false
root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
end
end
return '[http://kulturnav.org/'..id..' '..id..']'..p.getCatForId( 'KULTURNAV' ) --no https as of 9/2019
end
end


function p.lccnLink( id )
-- Categories in otherwise empty rows are collected in empty_row_categories.
local parts = p.splitLccn( id ) --e.g. n78039510
-- This function adds them to the module output. It is not affected by
if not parts then
-- args.decat because this module should not prevent module-external categories
return false
-- from rendering.
local function renderEmptyRowCategories()
for _, s in ipairs(empty_row_categories) do
root:wikitext(s)
end
end
local lccnType = parts[1] ~= 'sh' and 'names' or 'subjects'
id = parts[1] .. parts[2] .. p.append( parts[3], '0', 6 )
return '[https://id.loc.gov/authorities/'..lccnType..'/'..id..' '..id..']'..p.getCatForId( 'LCCN' )
end
end


function p.lirLink( id )
-- Render tracking categories. args.decat == turns off tracking categories.
--P886's format regex: \d+ (e.g. 1)
local function renderTrackingCategories()
if not id:match( '^%d+$' ) then
if args.decat == 'yes' then return end
return false
if args.child == 'yes' then
if args.title then
root:wikitext(
'[[Category:Pages which use embedded infobox templates with the title parameter]]'
)
end
elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
end
end
return '[http://www.e-lir.ch/e-LIR___Lexicon.'..id..'.450.0.html '..id..']'..p.getCatForId( 'LIR' ) --no https as of 9/2019
end
end


function p.lnbLink( id )
--[=[
--P1368's format regex: \d{9} (e.g. 123456789)
Loads the templatestyles for the infobox.
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
return false
end
return '[https://kopkatalogs.lv/F?func=direct&local_base=lnc10&doc_number='..id..'&P_CON_LNG=ENG '..id..']'..p.getCatForId( 'LNB' )
end


function p.leonoreLink( id )
TODO: load base templatestyles here rather than in MediaWiki:Common.css
--P640's format regex: LH/\d{1,4}/\d{1,3}|19800035/\d{1,4}/\d{1,5}(Bis)?|C/0/\d{1,2} (e.g. LH/2064/18)
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
if not id:match( '^LH/%d%d?%d?%d?/%d%d?%d?$' ) and            --IDs from      LH/1/1 to        LH/2794/54 (legionaries)
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
  not id:match( '^19800035/%d%d?%d?%d?/%d%d?%d?%d?%d?$' ) and --IDs from 19800035/1/1 to 19800035/385/51670 (legionnaires who died 1954-1977 & some who died < 1954)
When we do this we should clean up the inline CSS below too.
  not id:match( '^C/0/%d%d?$' ) then                          --IDs from        C/0/1 to            C/0/84 (84 famous legionaries)
Will have to do some bizarre conversion category like with sidebar.
return false
]=]
local function loadTemplateStyles()
local frame = mw.getCurrentFrame()
-- See function description
-- local base_templatestyles = frame:extensionTag{
-- name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
-- }
local templatestyles = ''
if args['templatestyles'] then templatestyles = frame:extensionTag{
name = 'templatestyles', args = { src = args['templatestyles'] }
}
end
end
return '[http://www.culture.gouv.fr/public/mistral/leonore_fr?ACTION=CHERCHER&FIELD_1=COTE&VALUE_1='..id..' '..id..']'..p.getCatForId( 'Léonore' ) --no https as of 9/2019
end
local child_templatestyles = ''
 
if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
function p.maLink( id )
name = 'templatestyles', args = { src = args['child templatestyles'] }
--P6366's format regex: [1-9]\d{4,9} (e.g. 1498221862)
}
if not id:match( '^[1-9]%d%d%d%d%d?%d?%d?%d?%d?$' ) then
return false
end
end
return '[https://academic.microsoft.com/v2/detail/'..id..' '..id..']'..p.getCatForId( 'MA' )
end
local grandchild_templatestyles = ''
 
if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
function p.mbaLink( id )
name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
--P434's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
}
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
return false
end
end
return '[https://musicbrainz.org/artist/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz' ) --special category name
end


function p.mbareaLink( id )
return table.concat({
--P982's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
-- base_templatestyles, -- see function description
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
templatestyles,
return false
child_templatestyles,
end
grandchild_templatestyles
return '[https://musicbrainz.org/area/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz area' ) --special category name
})
end
end


function p.mbiLink( id )
-- Specify the overall layout of the infobox, with special settings if the
--P1330's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
-- infobox is used as a 'child' inside another infobox.
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
local function _infobox()
return false
if args.child ~= 'yes' then
end
root = mw.html.create('table')
return '[https://musicbrainz.org/instrument/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz instrument' ) --special category name
end


function p.mblLink( id )
root
--P966's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
:addClass(args.bodyclass)
return false
-- @deprecated next; target .infobox-<name>
end
:cssText(args.bodystyle)
return '[https://musicbrainz.org/label/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz label' ) --special category name
end


function p.mbpLink( id )
renderTitle()
--P1004's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
renderAboveRow()
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
else
return false
root = mw.html.create()
end
return '[https://musicbrainz.org/place/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz place' ) --special category name
end


function p.mbrgLink( id )
root
--P436's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
:wikitext(args.title)
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
return false
end
end
return '[https://musicbrainz.org/release-group/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz release group' ) --special category name
end


function p.mbsLink( id )
renderSubheaders()
--P1407's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
renderImages()
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
preprocessRows()
return false
renderRows()
end
renderBelowRow()
return '[https://musicbrainz.org/series/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz series' ) --special category name
renderNavBar()
end
renderItalicTitle()
renderEmptyRowCategories()
renderTrackingCategories()


function p.mbwLink( id )
return loadTemplateStyles() .. tostring(root)
--P435's format regex: [0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12} (e.g. 12345678-1234-1234-1234-1234567890AB)
if not id:match( '^%x%x%x%x%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%-%x%x%x%x%x%x%x%x%x%x%x%x$' ) then
return false
end
return '[https://musicbrainz.org/work/'..id..' '..id..']'..p.getCatForId( 'MusicBrainz work' ) --special category name
end
end


function p.mgpLink( id )
-- If the argument exists and isn't blank, add it to the argument table.
--P549's format regex: \d{1,6} (e.g. 123456)
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
if not id:match( '^%d%d?%d?%d?%d?%d?$' ) then
local function preprocessSingleArg(argName)
return false
if origArgs[argName] and origArgs[argName] ~= '' then
args[argName] = origArgs[argName]
end
end
return '[https://genealogy.math.ndsu.nodak.edu/id.php?id='..id..' '..id..']'..p.getCatForId( 'MGP' )
end
end


function p.naraLink( id )
-- Assign the parameters with the given prefixes to the args table, in order, in
--P1225's format regex: ^([1-9]\d{0,8})$ (e.g. 123456789)
-- batches of the step size specified. This is to prevent references etc. from
if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
-- appearing in the wrong order. The prefixTable should be an array containing
return false
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
if type(prefixTable) ~= 'table' then
error("Non-table value detected for the prefix table", 2)
end
end
return '[https://catalog.archives.gov/id/'..id..' '..id..']'..p.getCatForId( 'NARA' )
if type(step) ~= 'number' then
end
error("Invalid step value detected", 2)
 
function p.nclLink( id )
--P1048's format regex: \d+ (e.g. 1081436)
if not id:match( '^%d+$' ) then
return false
end
end
return '[http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NCL' ) --no https as of 9/2019
end


function p.ndlLink( id )
-- Get arguments without a number suffix, and check for bad input.
--P349's format regex: 0?\d{8} (e.g. 012345678)
for i,v in ipairs(prefixTable) do
if not id:match( '^0?%d%d%d%d%d%d%d%d$' ) then
if type(v) ~= 'table' or type(v.prefix) ~= "string" or
return false
(v.depend and type(v.depend) ~= 'table') then
error('Invalid input detected to preprocessArgs prefix table', 2)
end
preprocessSingleArg(v.prefix)
-- Only parse the depend parameter if the prefix parameter is present
-- and not blank.
if args[v.prefix] and v.depend then
for j, dependValue in ipairs(v.depend) do
if type(dependValue) ~= 'string' then
error('Invalid "depend" parameter value detected in preprocessArgs')
end
preprocessSingleArg(dependValue)
end
end
end
end
return '[https://id.ndl.go.jp/auth/ndlna/'..id..' '..id..']'..p.getCatForId( 'NDL' )
end


function p.ngvLink( id )
-- Get arguments with number suffixes.
--P2041's format regex: \d+ (e.g. 12354)
local a = 1 -- Counter variable.
if not id:match( '^%d+$' ) then
local moreArgumentsExist = true
return false
while moreArgumentsExist == true do
end
moreArgumentsExist = false
return '[https://www.ngv.vic.gov.au/explore/collection/artist/'..id..'/ '..id..']'..p.getCatForId( 'NGV' )
for i = a, a + step - 1 do
end
for j,v in ipairs(prefixTable) do
 
local prefixArgName = v.prefix .. tostring(i)
function p.nkcLink( id )
if origArgs[prefixArgName] then
--P691's format regex: [a-z]{2,4}[0-9]{2,14} (e.g. abcd12345678901234)
-- Do another loop if any arguments are found, even blank ones.
if not id:match( '^[a-z][a-z][a-z]?[a-z]?%d%d%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
moreArgumentsExist = true
return false
preprocessSingleArg(prefixArgName)
end
end
return '[https://aleph.nkp.cz/F/?func=find-c&local_base=aut&ccl_term=ica='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'NKC' )
-- Process the depend table if the prefix argument is present
end
-- and not blank, or we are processing "prefix1" and "prefix" is
 
-- present and not blank, and if the depend table is present.
function p.nlaLink( id )
if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
--P409's format regex: [1-9][0-9]{0,11} (e.g. 123456789012)
for j,dependValue in ipairs(v.depend) do
if not id:match( '^[1-9]%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
local dependArgName = dependValue .. tostring(i)
return false
preprocessSingleArg(dependArgName)
end
end
return '[https://nla.gov.au/anbd.aut-an'..id..' '..id..']'..p.getCatForId( 'NLA' )
end
end
 
function p.nlgLink( id )
--P3348's format regex: [1-9]\d* (e.g. 1)
if not id:match( '^[1-9]%d*$' ) then
return false
end
return '[https://data.nlg.gr/resource/authority/record'..id..' '..id..']'..p.getCatForId( 'NLG' )
end
 
function p.nliLink( id )
--P949's format regex: \d{9} (e.g. 123456789)
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
return false
end
return '[http://uli.nli.org.il/F/?func=direct&doc_number='..id..'&local_base=nlx10'..' '..id..']'..p.getCatForId( 'NLI' )
end
 
function p.nlkLink( id )
--P5034's format regex: KA.(19|20).{7} (e.g. KAC201501465)
if not id:match( '^KA.19.......$' ) and
  not id:match( '^KA.20.......$' ) then
return false
end
return '[https://nl.go.kr/authorities/resource/'..id..' '..id..']'..p.getCatForId( 'NLK' )
end
 
function p.nlpLink( id )
--P1695's format regex: 9810[0-9]\d* or A[0-9]{7}[0-9X] (e.g. 9810123456789012345 or A10414836)
if not id:match( '^9810%d+$' ) and
  not id:match( '^A%d%d%d%d%d%d%d[%dX]$' ) then
return false
end
return '[https://tools.wmflabs.org/wikidata-externalid-url?p=1695&id='..id..' '..id..']'..p.getCatForId( 'NLP' )
end
 
function p.nlrLink( id )
--P1003's format regex: \d{9} (e.g. 123456789)
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
return false
end
return '[http://aleph.bibnat.ro:8991/F/?func=direct&local_base=NLR10&doc_number='..id..']'..p.getCatForId( 'NLR' )
end
 
function p.nskLink( id )
--P1375's format regex: \d{9} (e.g. 123456789)
if not id:match( '^%d%d%d%d%d%d%d%d%d$' ) then
return false
end
return '[http://katalog.nsk.hr/F/?func=direct&doc_number='..id..'&local_base=nsk10 '..id..']'..p.getCatForId( 'NSK' ) --no https as of 9/2019
end
 
function p.ntaLink( id )
--P1006's format regex: \d{8}[\dX] (e.g. 12345678X)
if not id:match( '^%d%d%d%d%d%d%d%d[%dX]$' ) then
return false
end
return '[http://data.bibliotheken.nl/id/thes/p'..id..' '..id..']'..p.getCatForId( 'NTA' )
end
 
function p.orcidLink( id )
id = p.validateIsni( id ) --e.g. 0000-0002-7398-5483
if not id then
return false
end
id = id:sub( 1, 4 )..'-'..id:sub( 5, 8 )..'-'..id:sub( 9, 12 )..'-'..id:sub( 13, 16 )
return '[https://orcid.org/'..id..' '..id..']'..p.getCatForId( 'ORCID' )
end
 
function p.plwabnLink( id )
--P7293's format regex: 981[0-9]{8}05606 (e.g. 9810696457305606)
if not id:match( '^981%d%d%d%d%d%d%d%d05606*$' ) then
return false
end
return '[http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=1&NU=1&IM=4&WI='..id..' '..id..']'..p.getCatForId( 'PLWABN' )
end
 
function p.publonsLink( id )
--P3829's format regex: \d+ (e.g. 654601)
if not id:match( '^%d+$' ) then
return false
end
return '[https://publons.com/author/'..id..'/ '..id..']'..p.getCatForId( 'Publons' )
end
 
function p.picLink( id )
--P2750's format regex: [1-9]\d* (e.g. 1)
if not id:match( '^[1-9]%d*$' ) then
return false
end
return '[https://pic.nypl.org/constituents/'..id..' '..id..']'..p.getCatForId( 'PIC' )
end
 
function p.ridLink( id )
--P1053's format regex: [A-Z]{1,3}-\d{4}-(19|20)\d\d (e.g. AAS-5150-2020)
if not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-19%d%d$' ) and
  not id:match( '^[A-Z][A-Z]?[A-Z]?%-%d%d%d%d%-20%d%d$' ) then
return false
end
return '[https://www.researcherid.com/rid/'..id..' '..id..']'..p.getCatForId( 'RID' )
end
 
function p.reroLink( id )
--P3065's format regex: 0[1-2]-[A-Z0-9]{1,10} (e.g. 02-A012345678)
if not id:match( '^0[1-2]%-[A-Z%d][A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?[A-Z%d]?$' ) then
return false
end
return '[http://data.rero.ch/'..id..' '..id..']'..p.getCatForId( 'RERO' )
end
 
function p.rkdartistsLink( id )
--P650's format regex: [1-9]\d{0,5} (e.g. 123456)
if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
return false
end
return '[https://rkd.nl/en/explore/artists/'..id..' '..id..']'..p.getCatForId( 'RKDartists' )
end
 
function p.rkdidLink( id )
--P350's format regex: [1-9]\d{0,5} (e.g. 123456)
if not id:match( '^[1-9]%d?%d?%d?%d?%d?$' ) then
return false
end
return '[https://rkd.nl/nl/explore/images/'..id..' '..id..']'..p.getCatForId( 'RKDID' )
end
 
function p.rslLink( id )
--P947's format regex: \d{1,9} (e.g. 123456789)
if not id:match( '^%d%d?%d?%d?%d?%d?%d?%d?%d?$' ) then
return false
end
return '[http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request='..id..'&CON_LNG=ENG '..id..']'..p.getCatForId( 'RSL' ) --no https as of 9/2019
end
 
function p.iccuLink( id )
--P396's format regex: IT\\ICCU\\(\d{10}|\D\D[\D\d]\D\\\d{6}) (e.g. IT\ICCU\CFIV\000163)
if not id:match( '^IT\\ICCU\\%d%d%d%d%d%d%d%d%d%d$' ) and
  not id:match( '^IT\\ICCU\\%u%u[%u%d]%u\\%d%d%d%d%d%d$' ) then --legacy: %u used here instead of %D (but the faulty ID cat is empty, out of ~12k uses)
return false
end
return '[https://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid='..id..' '..id..']'..p.getCatForId( 'ICCU' ) end
 
function p.selibrLink( id )
--P906's format regex: [1-9]\d{4,5} (e.g. 123456)
if not id:match( '^[1-9]%d%d%d%d%d?$' ) then
return false
end
return '[https://libris.kb.se/auth/'..id..' '..id..']'..p.getCatForId( 'SELIBR' )
end
 
function p.sikartLink( id )
--P781's format regex: \d{7,9} (e.g. 123456789)
if not id:match( '^%d%d%d%d%d%d%d%d?%d?$' ) then
return false
end
return '[http://www.sikart.ch/KuenstlerInnen.aspx?id='..id..'&lng=en '..id..']'..p.getCatForId( 'SIKART' ) --no https as of 9/2019
end
 
function p.snacLink( id )
--P3430's format regex: \d*[A-Za-z][0-9A-Za-z]* (e.g. A)
if not id:match( '^%d*[A-Za-z][0-9A-Za-z]*$' ) then
return false
end
return '[https://snaccooperative.org/ark:/99166/'..id..' '..id..']'..p.getCatForId( 'SNAC-ID' )
end
 
function p.sudocLink( id )
--P269's format regex: (\d{8}[\dX]|) (e.g. 026927608)
if not id:match( '^%d%d%d%d%d%d%d%d[%dxX]$' ) then --legacy: allow lowercase 'x'
return false
end
return '[https://www.idref.fr/'..id..' '..id..']'..p.getCatForId( 'SUDOC' )
end
 
function p.s2authoridLink( id )
--P4012's format regex: [1-9]\d* (e.g. 1796130)
if not id:match( '^[1-9]%d*$' ) then
return false
end
return '[https://www.semanticscholar.org/author/'..id..' '..id..']'..p.getCatForId( 'Semantic Scholar author' ) --special category name
end
 
function p.ta98Link( id )
--P1323's format regex: A\d{2}\.\d\.\d{2}\.\d{3}[FM]? (e.g. A12.3.45.678)
if not id:match( '^A%d%d%.%d%.%d%d%.%d%d%d[FM]?$' ) then
return false
end
return '[http://tools.wmflabs.org/wikidata-externalid-url/?p=1323&url_prefix=https:%2F%2Fwww.unifr.ch%2Fifaa%2FPublic%2FEntryPage%2FTA98%20Tree%2FEntity%20TA98%20EN%2F&url_suffix=%20Entity%20TA98%20EN.htm&id='..id..' '..id..']'..p.getCatForId( 'TA98' )
end
 
function p.tdviaLink( id )
--P7314's format regex: [a-z/-]+] (e.g. barkan-omer-lutfi)
if not id:match( '^[a-z/-]+$' ) then
return false
end
return '[https://islamansiklopedisi.org.tr/'..id..' '..id..']'..p.getCatForId( 'TDVİA' )
end
 
function p.teLink( id )
--P1693's format regex: E[1-8]\.\d{1,2}\.\d{1,2}\.\d{1,2}\.\d{1}\.\d{1}\.\d{1,3} (e.g. E1.23.45.67.8.9.0)
local e1, e2 = id:match( '^E([1-8])%.(%d%d?)%.%d%d?%.%d%d?%.%d%.%d%.%d%d?%d?$' )
if not e1 then
return false
end
local TEnum = 'TEe0'..e1 --no formatter URL in WD, probably due to this complexity
if e1 == '5' or e1 == '7' then
if #e2 == 1 then e2 = '0'..e2 end
TEnum = TEnum..e2
end
return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTE/'..TEnum..'.html '..id..']'..p.getCatForId( 'TE' )
end
 
function p.tepapaLink( id )
--P3544's format regex: \d+ (e.g. 1)
if not id:match( '^%d+$' ) then
return false
end
return '[https://collections.tepapa.govt.nz/agent/'..id..' '..id..']'..p.getCatForId( 'TePapa' )
end
 
function p.thLink( id )
--P1694's format regex: H\d\.\d{2}\.\d{2}\.\d\.\d{5} (e.g. H1.23.45.6.78901)
local h1, h2 = id:match( '^H(%d)%.(%d%d)%.%d%d%.%d%.%d%d%d%d%d$' )
if not h1 then
return false
end
local THnum = 'THh'..h1..h2 --no formatter URL in WD, probably due to this complexity
return '[http://www.unifr.ch/ifaa/Public/EntryPage/ViewTH/'..THnum..'.html '..id..']'..p.getCatForId( 'TH' )
end
 
function p.tlsLink( id )
local id2 = id:gsub(' +', '_')
--P1362's format regex: \p{Lu}[\p{L}\d_',\.\-\(\)\*/–]{3,59} (e.g. Abcd)
local class = "[%a%d_',%.%-%(%)%*/–]"
local regex = "^%u"..string.rep(class, 3)..string.rep(class.."?", 56).."$"
if not mw.ustring.match( id2, regex ) then
return false
end
return '[http://tls.theaterwissenschaft.ch/wiki/'..id2..' '..id..']'..p.getCatForId( 'TLS' ) --no https as of 9/2019
end
 
function p.troveLink( id )
--P1315's format regex: [1-9]\d{5,7} (e.g. 12345678)
if not id:match( '^[1-9]%d%d%d%d%d%d?%d?$' ) then
return false
end
return '[https://trove.nla.gov.au/people/'..id..' '..id..']'..p.getCatForId( 'Trove' )
end
 
function p.ukparlLink( id )
--P6213's format regex: [a-zA-Z\d]{8} (e.g. AQUupyiR)
if not id:match( '^[a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d][a-zA-Z%d]$' ) then
return false
end
return '[https://id.parliament.uk/'..id..' '..id..']'..p.getCatForId( 'UKPARL' )
end
 
function p.ulanLink( id )
--P245's format regex: 500\d{6} (e.g. 500123456)
if not id:match( '^500%d%d%d%d%d%d$' ) then
return false
end
return '[https://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid='..id..' '..id..']'..p.getCatForId( 'ULAN' )
end
 
function p.uscongressLink( id )
--P1157's format regex: [A-Z]00[01]\d{3} (e.g. A000123)
if not id:match( '^[A-Z]00[01]%d%d%d$' ) then
return false
end
return '[http://bioguide.congress.gov/scripts/biodisplay.pl?index='..id..' '..id..']'..p.getCatForId( 'USCongress' ) --no https as of 9/2019
end
 
function p.vcbaLink( id )
--P8034's format regex: \d{3}\/[1-9]\d{0,5} (e.g. 494/9793)
if not id:match( '^%d%d%d\/[1-9]%d?%d?%d?%d?%d?$' ) then
return false
end
local id2 = id:gsub('\/', '_')
return '[https://opac.vatlib.it/auth/detail/'..id2..' '..id..']'..p.getCatForId( 'VcBA' )
end
 
function p.viafLink( id )
--P214's format regex: [1-9]\d(\d{0,7}|\d{17,20}) (e.g. 123456789, 1234567890123456789012)
if not id:match( '^[1-9]%d%d?%d?%d?%d?%d?%d?%d?$' ) and
  not id:match( '^[1-9]%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d?%d?%d?$' ) then
return false
end
-- If the "VIAF" entry at [[:m:Interwiki map]] would resolve to "https://viaf.org/viaf/$1" (rather than "http://viaf.org/viaf/$1", as it currently still does), the code below could change from '[https://viaf.org/viaf/'..id..' '..id..']' to '[[:VIAF:'..id..'|'..id..']]'.
return '[https://viaf.org/viaf/'..id..' '..id..']'..p.getCatForId( 'VIAF' )
end
 
--[[=========================== Helper functions =============================]]
 
function p.append(str, c, length)
while str:len() < length do
str = c .. str
end
return str
end
 
--Returns the ISNI check digit isni must be a string where the 15 first elements are digits, e.g. 0000000066534145
function p.getIsniCheckDigit( isni )
local total = 0
for i = 1, 15 do
local digit = isni:byte( i ) - 48 --Get integer value
total = (total + digit) * 2
end
local remainder = total % 11
local result = (12 - remainder) % 11
if result == 10 then
return "X"
end
return tostring( result )
end
 
--Validate ISNI (and ORCID) and retuns it as a 16 characters string or returns false if it's invalid
--See http://support.orcid.org/knowledgebase/articles/116780-structure-of-the-orcid-identifier
function p.validateIsni( id )
--P213 (ISNI) format regex: [0-9]{4} [0-9]{4} [0-9]{4} [0-9]{3}[0-9X] (e.g. 0000-0000-6653-4145)
--P496 (ORCID) format regex: 0000-000(1-[5-9]|2-[0-9]|3-[0-4])\d{3}-\d{3}[\dX] (e.g. 0000-0002-7398-5483)
id = id:gsub( '[ %-]', '' ):upper()
if not id:match( '^%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d[%dX]$' ) then
return false
end
if p.getIsniCheckDigit( id ) ~= string.char( id:byte( 16 ) ) then
return false
end
return id
end
 
function p.splitLccn( id )
--P244's format regex: (n|nb|nr|no|ns|sh)([4-9][0-9]|00|20[0-1][0-9])[0-9]{6} (e.g. n78039510)
if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
end
if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
return mw.text.split( id, '/' )
end
return false
end
 
--[[==========================================================================]]
--[[                    Wikidata & documentation functions                    ]]
--[[==========================================================================]]
 
function p.getIdsFromWikidata( itemId, property )
local ids = {}
local statements = mw.wikibase.getBestStatements( itemId, property )
if statements then
for _, statement in ipairs( statements ) do
if statement.mainsnak.datavalue then
table.insert( ids, statement.mainsnak.datavalue.value )
end
end
end
end
a = a + step
end
end
return ids
end
end


function p.matchesWikidataRequirements( itemId, reqs )
-- Parse the data parameters in the same order that the old {{infobox}} did, so
for _, group in ipairs( reqs ) do
-- that references etc. will display in the expected places. Parameters that
local property = 'P'..group[1]
-- depend on another parameter are only processed if that parameter is present,
local qid = group[2]
-- to avoid phantom references appearing in article reference lists.
local statements = mw.wikibase.getBestStatements( itemId, property )
local function parseDataParameters()
if statements then
 
for _, statement in ipairs( statements ) do
preprocessSingleArg('autoheaders')
if statement.mainsnak.datavalue then
preprocessSingleArg('child')
if statement.mainsnak.datavalue.value['numeric-id'] == qid then
preprocessSingleArg('bodyclass')
return true
preprocessSingleArg('subbox')
end end end end end
preprocessSingleArg('bodystyle')
return false
preprocessSingleArg('title')
preprocessSingleArg('titleclass')
preprocessSingleArg('titlestyle')
preprocessSingleArg('above')
preprocessSingleArg('aboveclass')
preprocessSingleArg('abovestyle')
preprocessArgs({
{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
}, 10)
preprocessSingleArg('subheaderstyle')
preprocessSingleArg('subheaderclass')
preprocessArgs({
{prefix = 'image', depend = {'caption', 'imagerowclass'}}
}, 10)
preprocessSingleArg('captionstyle')
preprocessSingleArg('imagestyle')
preprocessSingleArg('imageclass')
preprocessArgs({
{prefix = 'header'},
{prefix = 'data', depend = {'label'}},
{prefix = 'rowclass'},
{prefix = 'rowstyle'},
{prefix = 'rowcellstyle'},
{prefix = 'class'}
}, 50)
preprocessSingleArg('headerclass')
preprocessSingleArg('headerstyle')
preprocessSingleArg('labelstyle')
preprocessSingleArg('datastyle')
preprocessSingleArg('below')
preprocessSingleArg('belowclass')
preprocessSingleArg('belowstyle')
preprocessSingleArg('name')
-- different behaviour for italics if blank or absent
args['italic title'] = origArgs['italic title']
preprocessSingleArg('decat')
preprocessSingleArg('templatestyles')
preprocessSingleArg('child templatestyles')
preprocessSingleArg('grandchild templatestyles')
end
end


-- Creates a human-readable standalone wikitable version of p.conf, and tracking categories with page counts, for use in the documentation
-- If called via #invoke, use the args passed into the invoking template.
function p.docConfTable( frame )
-- Otherwise, for testing purposes, assume args are being passed directly in.
local wikiTable = '{| class="wikitable sortable"\n'..
function p.infobox(frame)
  '! rowspan=2 | Parameter\n'..
if frame == mw.getCurrentFrame() then
  '! rowspan=2 | Label\n'..
origArgs = frame:getParent().args
  '! rowspan=2; data-sort-type=number | Wikidata property\n'..
else
  '! colspan=4 | Tracking categories and page counts\n'..
origArgs = frame
  '|-\n'..
  '! [[:Category:Wikipedia articles with authority control information|'..      'Articles]]\n'..
  '! [[:Category:User pages with authority control information|'..              'User pages]]\n'..
  '! [[:Category:Miscellaneous pages with authority control information|'..      'Misc. pages]]\n'..
  '! [[:Category:Wikipedia articles with faulty authority control information|'..'Faulty IDs]]\n'..
  '|-\n'
local lang = mw.getContentLanguage()
for _, conf in pairs( p.conf ) do
local param, link, pid = conf[1], conf[2], conf[3]
local category = conf.category or param
local args = { id = 'f', pid }
local wpl = frame:expandTemplate{ title = 'Wikidata property link', args = args }
--cats
local articleCat = 'Wikipedia articles with '..category..' identifiers'
local userCat =    'User pages with '..category..' identifiers'
local miscCat =    'Miscellaneous pages with '..category..' identifiers'
local faultyCat =  'Wikipedia articles with faulty '..category..' identifiers'
--counts
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
local userCount =   lang:formatNum( mw.site.stats.pagesInCategory(userCat, 'pages') )
local miscCount =   lang:formatNum( mw.site.stats.pagesInCategory(miscCat, 'pages') )
local faultyCount =  lang:formatNum( mw.site.stats.pagesInCategory(faultyCat, 'pages') )
--concat
wikiTable = wikiTable..'\n'..
'|-\n'..
'||'..param..
'||'..link..
'||data-sort-value='..pid..'|'..wpl..
'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
'||style="text-align: right;"|[[:Category:'..  userCat..'|'..  userCount..']]'..
'||style="text-align: right;"|[[:Category:'..  miscCat..'|'..  miscCount..']]'..
'||style="text-align: right;"|[[:Category:'.. faultyCat..'|'.. faultyCount..']]'
end
end
--append derivative WorldCat cats
parseDataParameters()
local wcd = { 'WorldCat-LCCN', 'WorldCat-VIAF' }
for _, w in pairs(wcd) do
local articleCat = 'Wikipedia articles with '..w..' identifiers'
local articleCount = lang:formatNum( mw.site.stats.pagesInCategory(articleCat, 'pages') )
wikiTable = wikiTable..'\n'..
'|-\n'..
'||'..'—'..
'||'..w..
'||data-sort-value='..w..'|'..'—'..
'||style="text-align: right;"|[[:Category:'..articleCat..'|'..articleCount..']]'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'..
'||style="text-align: right;"|—'
end
return wikiTable..'\n|}'
return _infobox()
end
end


--[[==========================================================================]]
-- For calling via #invoke within a template
--[[                              Configuration                              ]]
function p.infoboxTemplate(frame)
--[[==========================================================================]]
origArgs = {}
 
for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
-- If a specific "(identifier) redirect" exists for an identifier, please route through this particular redirect rather than linking directly to the target page. This reduces clutter in "What links here" and improves reverse lookup of articles where a manifestation of this particular identifier is used.
 
-- Check that the Wikidata item has this property-->value before adding it
local reqs = {}
 
-- Parameter format: { 'parameter name', 'label', propertyId # in Wikidata, formatting/validation function }
p.conf = {
{ 'AAG', '[[AAG (identifier)|AAG]]', 3372, p.aagLink },
{ 'ACM-DL', '[[ACM DL (identifier)|ACM DL]]', 864, p.acmLink },
{ 'ADB', '[[ADB (identifier)|ADB]]', 1907, p.adbLink },
{ 'AGSA', '[[AGSA (identifier)|AGSA]]', 6804, p.agsaLink },
{ 'autores.uy', '[[autores.uy (identifier)|autores.uy]]', 2558, p.autoresuyLink },
{ 'AWR', '[[AWR (identifier)|AWR]]', 4186, p.awrLink },
{ 'BALaT', '[[BALaT (identifier)|BALaT]]', 3293, p.balatLink },
{ 'BIBSYS', '[[BIBSYS (identifier)|BIBSYS]]', 1015, p.bibsysLink },
{ 'Bildindex', '[[Bildindex (identifier)|Bildindex]]', 2092, p.bildLink },
{ 'BNC', '[[BNC (identifier)|BNC]]', 1890, p.bncLink },
{ 'BNE', '[[BNE (identifier)|BNE]]', 950, p.bneLink },
{ 'BNF', '[[BNF (identifier)|BNF]]', 268, p.bnfLink },
{ 'Botanist', '[[Botanist (identifier)|Botanist]]', 428, p.botanistLink },
{ 'BPN', '[[BPN (identifier)|BPN]]', 651, p.bpnLink },
{ 'CANTIC', '[[CANTIC (identifier)|CANTIC]]', 1273, p.canticLink },
{ 'CINII', '[[CiNii (identifier)|CiNii]]', 271, p.ciniiLink },
{ 'CWGC', '[[CWGC (identifier)|CWGC]]', 1908, p.cwgcLink },
{ 'DAAO', '[[DAAO (identifier)|DAAO]]', 1707, p.daaoLink },
{ 'DBLP', '[[DBLP (identifier)|DBLP]]', 2456, p.dblpLink },
{ 'DIB', '[[DIB (identifier)|DIB]]', 6829, p.dibLink },
{ 'DSI', '[[DSI (identifier)|DSI]]', 2349, p.dsiLink },
{ 'FNZA', '[[FNZA (identifier)|FNZA]]', 6792, p.fnzaLink }, --wd
{ 'GND', '[[GND (identifier)|GND]]', 227, p.gndLink },
{ 'HDS', '[[HDS (identifier)|HDS]]', 902, p.hdsLink },
{ 'IAAF', '[[IAAF (identifier)|IAAF]]', 1146, p.iaafLink },
{ 'ICCU', '[[ICCU (identifier)|ICCU]]', 396, p.iccuLink }, --formerly SBN
{ 'ICIA', '[[ICIA (identifier)|ICIA]]', 1736, p.iciaLink },
{ 'IEU', '[[IEU (identifier)|IEU]]', 9070, p.ieuLink },
{ 'ISNI', '[[ISNI (identifier)|ISNI]]', 213, p.isniLink },
{ 'Joconde', '[[Joconde (identifier)|Joconde]]' , 347, p.jocondeLink },
{ 'KULTURNAV', '[[KulturNav (identifier)|KulturNav]]', 1248, p.kulturnavLink },
{ 'LCCN', '[[LCCN (identifier)|LCCN]]', 244, p.lccnLink },
{ 'LIR', '[[LIR (identifier)|LIR]]', 886, p.lirLink },
{ 'LNB', '[[LNB (identifier)|LNB]]', 1368, p.lnbLink },
{ 'Léonore', '[[Léonore (identifier)|Léonore]]', 640, p.leonoreLink },
{ 'MA', '[[MA (identifier)|MA]]', 6366, p.maLink },
{ 'MBA', '[[MBA (identifier)|MBA]]', 434, p.mbaLink, category = 'MusicBrainz' }, --special category name
{ 'MBAREA', '[[MBAREA (identifier)|MBAREA]]', 982, p.mbareaLink, category = 'MusicBrainz area' }, --special category name
{ 'MBI', '[[MBI (identifier)|MBI]]', 1330, p.mbiLink, category = 'MusicBrainz instrument' }, --special category name
{ 'MBL', '[[MBL (identifier)|MBL]]', 966, p.mblLink, category = 'MusicBrainz label' }, --special category name
{ 'MBP', '[[MBP (identifier)|MBP]]', 1004, p.mbpLink, category = 'MusicBrainz place' }, --special category name
{ 'MBRG', '[[MBRG (identifier)|MBRG]]', 436, p.mbrgLink, category = 'MusicBrainz release group' }, --special category name
{ 'MBS', '[[MBS (identifier)|MBS]]', 1407, p.mbsLink, category = 'MusicBrainz series' }, --special category name
{ 'MBW', '[[MBW (identifier)|MBW]] work', 435, p.mbwLink, category = 'MusicBrainz work' }, --special category name
{ 'MGP', '[[MGP (identifier)|MGP]]', 549, p.mgpLink },
{ 'NARA', '[[NARA (identifier)|NARA]]', 1225, p.naraLink },
{ 'NCL', '[[NCL (identifier)|NBL]]', 1048, p.nclLink },
{ 'NDL', '[[NDL (identifier)|NDL]]', 349, p.ndlLink },
{ 'NGV', '[[NGV (identifier)|NGV]]', 2041, p.ngvLink },
{ 'NKC', '[[NKC (identifier)|NKC]]', 691, p.nkcLink },
{ 'NLA', '[[NLA (identifier)|NLA]]', 409, p.nlaLink },
{ 'NLG', '[[NLG (identifier)|NLG]]', 3348, p.nlgLink },
{ 'NLI', '[[NLI (identifier)|NLI]]', 949, p.nliLink },
{ 'NLK', '[[NLK (identifier)|NLK]]', 5034, p.nlkLink },
{ 'NLP', '[[NLP (identifier)|NLP]]', 1695, p.nlpLink },
{ 'NLR', '[[NLR (identifier)|NLR]]', 1003, p.nlrLink },
{ 'NSK', '[[NSK (identifier)|NSK]]', 1375, p.nskLink },
{ 'NTA', '[[NTA (identifier)|NTA]]', 1006, p.ntaLink },
{ 'ORCID', '[[ORCID (identifier)|ORCID]]', 496, p.orcidLink },
{ 'PIC', '[[PIC (identifier)|PIC]]', 2750, p.picLink }, --wd
{ 'PLWABN', '[[PLWABN (identifier)|PLWABN]]', 7293, p.plwabnLink },
{ 'Publons', '[[Publons (identifier)|Publons]]', 3829, p.publonsLink },
{ 'RID', '[[RID (identifier)|ResearcherID]]', 1053, p.ridLink },
{ 'RERO', '[[RERO (identifier)|RERO]]', 3065, p.reroLink },
{ 'RKDartists', '[[RKDartists (identifier)|RKD]]', 650, p.rkdartistsLink },
{ 'RKDID', '[[RKDID (identifier)|RKDimages ID]]', 350, p.rkdidLink },
{ 'RSL', '[[RSL (identifier)|RSL]]', 947, p.rslLink },
{ 'SELIBR', '[[SELIBR (identifier)|SELIBR]]', 906, p.selibrLink },
{ 'SIKART', '[[SIKART (identifier)|SIKART]]', 781, p.sikartLink },
{ 'SNAC-ID', '[[SNAC-ID (identifier)|SNAC]]', 3430, p.snacLink },
{ 'SUDOC', '[[SUDOC (identifier)|SUDOC]]', 269, p.sudocLink },
{ 'S2AuthorId', '[[S2AuthorId (identifier)|S2AuthorId]]', 4012, p.s2authoridLink, category = 'Semantic Scholar author' }, --special category name
{ 'TA98', '[[TA98 (identifier)|TA98]]', 1323, p.ta98Link },
{ 'TDVİA', '[[TDVİA (identifier)|TDVİA]]', 7314, p.tdviaLink },
{ 'TE', '[[TE (identifier)|TE]]', 1693, p.teLink },
{ 'TePapa', '[[TePapa (identifier)|TePapa]]', 3544, p.tepapaLink },
{ 'TH', '[[TH (identifier)|TH]]', 1694, p.thLink },
{ 'TLS', '[[TLS (identifier)|TLS]]', 1362, p.tlsLink },
{ 'Trove', '[[Trove (identifier)|Trove]]', 1315, p.troveLink }, --formerly NLA-person
{ 'UKPARL', '[[UKPARL (identifier)|UKPARL]]', 6213, p.ukparlLink },
{ 'ULAN', '[[ULAN (identifier)|ULAN]]', 245, p.ulanLink },
{ 'USCongress', '[[US Congress (identifier)|US Congress]]', 1157, p.uscongressLink },
{ 'VcBA', '[[VcBA (identifier)|VcBA]]', 8034, p.vcbaLink },
{ 'VIAF', '[[VIAF (identifier)|VIAF]]', 214, p.viafLink },
{ 'WORLDCATID', '[[WorldCat Identities (identifier)|WorldCat Identities]]', 7859, nil },
}
 
-- Legitimate aliases to p.conf, for convenience
-- Format: { 'alias', 'parameter name in p.conf' }
p.aliases = {
{ 'DNB', 'GND' }, --Deutsche Nationalbibliothek -> Gemeinsame Normdatei
{ 'Leonore', 'Léonore' }, --alias name without diacritics
{ 'leonore', 'Léonore' }, --lowercase variant without diacritics
{ 'MusicBrainz', 'MBA' },
{ 'MusicBrainz artist', 'MBA' },
{ 'MusicBrainz label', 'MBL' },
{ 'MusicBrainz release group', 'MBRG' },
{ 'MusicBrainz work', 'MBW' },
{ 'SBN', 'ICCU' }, --SBN alias to be deprecated at a later stage
{ 'TDVIA', 'TDVİA' }, --alias name without diacritics
{ 'tdvia', 'TDVİA' }, --lowercase variant without diacritics
}
 
-- Deprecated aliases to p.conf; tracked in [[Category:Wikipedia articles with deprecated authority control identifiers]]
-- Format: { 'deprecated parameter name', 'replacement parameter name in p.conf' }
p.deprecated = {
{ 'GKD', 'GND' },
{ 'PND', 'GND' },
{ 'RLS', 'RSL' },
{ 'SWD', 'GND' },
{ 'NARA-organization', 'NARA' },
{ 'NARA-person', 'NARA' },
}
 
--[[==========================================================================]]
--[[                                  Main                                  ]]
--[[==========================================================================]]
 
function p.authorityControl( frame )
local resolveEntity = require( "Module:ResolveEntityId" )
local parentArgs = frame:getParent().args --WD IDs added here later
local iParentArgs = 0 --count original/manual parent args only later
local elements = {} --create/insert rows later
local worldcatCat = ''
local multipleIdCat = ''
local suppressedIdCat = ''
local deprecatedIdCat = ''
local differentOnWDCat = ''
local sameOnWDCat = ''
--Redirect aliases to proper parameter names
for _, a in pairs( p.aliases ) do
local alias, param = a[1], a[2]
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[alias] then
parentArgs[param] = parentArgs[alias]
end
end
--Redirect deprecated parameters to proper parameter names, and assign tracking cat
for _, d in pairs( p.deprecated ) do
local dep, param = d[1], d[2]
if (parentArgs[param] == nil or parentArgs[param] == '') and parentArgs[dep] then
parentArgs[param] = parentArgs[dep]
if namespace == 0 then
deprecatedIdCat = '[[Category:Wikipedia articles with deprecated authority control identifiers|'..dep..']]'
end
end
end
--Use QID= parameter for testing/example purposes only
parseDataParameters()
local itemId = nil
if namespace ~= 0 then
local qid = parentArgs['qid'] or parentArgs['QID']
if qid then
itemId = 'Q'..mw.ustring.gsub(qid, '^[Qq]', '')
itemId = resolveEntity._id(itemId) --nil if unresolvable
end
else
itemId = mw.wikibase.getEntityIdForCurrentPage()
end
--Wikidata fallback if available
if itemId then
local iMatches = 0
for _, params in ipairs( p.conf ) do
if params[3] > 0 then
local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
if val == nil or val == '' then
local canUseWikidata = nil
if reqs[params[1]] then
--since reqs{} has been nil afaicr, p.matchesWikidataRequirements will never be called
--TODO: reqs{} doesn't seem useful/necessary; appendage of early WD incorporation?
canUseWikidata = p.matchesWikidataRequirements( itemId, reqs[params[1]] )
else
canUseWikidata = true
end
if canUseWikidata then
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
if wikidataIds[1] then
if val == '' and (namespace == 0 or testcases) then
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|'..params[1]..']]'
else
parentArgs[params[1]] = wikidataIds[1] --add ID from WD
end
end
end
else
iParentArgs = iParentArgs + 1
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
if wikidataIds[1] and differentOnWDCat == '' then
local bMatch = false
for _, wd in pairs( wikidataIds ) do
if val == wd then
iMatches = iMatches + 1
bMatch = true
end
end
if bMatch == false then
differentOnWDCat = '[[Category:Pages using authority control with parameters different on Wikidata|'..params[1]..']]'
end end end end end
if iMatches > 0 and iMatches == iParentArgs then
sameOnWDCat = '[[Category:Pages using authority control with parameters all matching Wikidata]]'
end
end
--Configured rows
return _infobox()
local rct = 0
for _, params in ipairs( p.conf ) do
local val = parentArgs[mw.ustring.lower(params[1])] or parentArgs[params[1]]
local tval, tlinks = {}, {} --init tables
if val and val ~= '' and type(params[4]) == 'function' then
table.insert( tval, val )
table.insert( tlinks, params[4]( val ) )
end
--collect other unique vals (IDs) from WD, if present
if itemId and tval[1] then
local wikidataIds = p.getIdsFromWikidata( itemId, 'P'..params[3] )
for _, v in pairs( wikidataIds ) do
local bnew = true
for _, w in pairs( tval ) do
if v == w then bnew = false end
end
if bnew then
table.insert( tval, v )
table.insert( tlinks, params[4]( v ) )
end
end
end
--assemble
if tval[1] then
table.insert( elements, p.createRow( params[1], params[2]..':', tval, nil, tlinks, true, params.category ) )
rct = rct + 1
if tval[2] then
multipleIdCat = p.getCatForId( 'multiple' )
end
end
end
--WorldCat
local worldcatId = parentArgs['worldcatid'] or parentArgs['WORLDCATID']
if worldcatId and worldcatId ~= '' then --if WORLDCATID present & unsuppressed
table.insert( elements, p.createRow( 'WORLDCATID', '', worldcatId, '[[WorldCat Identities (identifier)|WorldCat Identities]]: [https://www.worldcat.org/identities/'..mw.uri.encode(worldcatId, 'PATH')..' '..worldcatId..']', nil, false ) ) --Validation?
worldcatCat = p.getCatForId( 'WORLDCATID' )
elseif worldcatId == nil then --if WORLDCATID absent but unsuppressed
local viafId = parentArgs['viaf'] or parentArgs['VIAF']
local lccnId = parentArgs['lccn'] or parentArgs['LCCN']
if viafId and viafId ~= '' and p.viafLink( viafId ) then --VIAF must be present, unsuppressed, & validated
table.insert( elements, p.createRow( 'VIAF', '', viafId, '[[WorldCat Identities (identifier)|WorldCat Identities]] (via VIAF): [https://www.worldcat.org/identities/containsVIAFID/'..viafId..' '..viafId..']', nil, false ) )
if namespace == 0 then
worldcatCat = '[[Category:Wikipedia articles with WorldCat-VIAF identifiers]]'
end
elseif lccnId and lccnId ~= '' and p.lccnLink( lccnId ) then --LCCN must be present, unsuppressed, & validated
local lccnParts = p.splitLccn( lccnId )
if lccnParts and lccnParts[1] ~= 'sh' then
local lccnIdFmtd = lccnParts[1]..lccnParts[2]..'-'..lccnParts[3]
table.insert( elements, p.createRow( 'LCCN', '', lccnId, '[[WorldCat Identities (identifier)|WorldCat Identities]] (via LCCN): [https://www.worldcat.org/identities/lccn-'..lccnIdFmtd..' '..lccnIdFmtd..']', nil, false ) )
if namespace == 0 then
worldcatCat = '[[Category:Wikipedia articles with WorldCat-LCCN identifiers]]'
end
end
end
elseif worldcatId == '' then --if WORLDCATID suppressed
suppressedIdCat = '[[Category:Wikipedia articles with suppressed authority control identifiers|WORLDCATID]]'
end
local Navbox = require('Module:Navbox')
local elementsCat = ''
if rct == 0 or rct >= 25 then
local eCat = 'AC with '..rct..' elements'
elementsCat  = '[[Category:'..eCat..']]'..p.redCatLink(eCat)
end
local outString = ''
if #elements > 0 then
local args = { pid = 'identifiers' } -- #target the list of identifiers
if testcases and itemId then args = { pid = 'identifiers', qid = itemId } end --expensive
local pencil = frame:expandTemplate{ title = 'EditAtWikidata', args = args}
outString = Navbox._navbox( {
name  = 'Authority control',
navboxclass = 'authority-control',
bodyclass = 'hlist',
group1 = '[[Help:Authority control|Authority control]]'..pencil,
list1 = table.concat( elements )
} )
end
local auxCats = worldcatCat .. elementsCat .. multipleIdCat .. suppressedIdCat ..
deprecatedIdCat .. differentOnWDCat .. sameOnWDCat
if testcases then
auxCats = mw.ustring.gsub(auxCats, '(%[%[)(Category)', '%1:%2') --for easier checking
end
outString = outString .. auxCats
if namespace ~= 0 then
outString = mw.ustring.gsub(outString, '(%[%[)(Category:Wikipedia articles)', '%1:%2') --by definition
end
return outString
end
end
return p
return p

Revision as of 19:24, 31 March 2021

Module:Infobox is a module that implements the {{Infobox}} template. Please see the template page for usage instructions.

Tracking categories


local p = {}
local args = {}
local origArgs = {}
local root
local empty_row_categories = {}
local category_in_empty_row_pattern = '%[%[%s*[Cc][Aa][Tt][Ee][Gg][Oo][Rr][Yy]%s*:[^]]*]]'

local function fixChildBoxes(sval, tt)
	local function notempty( s ) return s and s:match( '%S' ) end
	
	if notempty(sval) then
		local marker = '<span class=special_infobox_marker>'
		local s = sval
		s = mw.ustring.gsub(s, '(<%s*[Tt][Rr])', marker .. '%1')
		s = mw.ustring.gsub(s, '(</[Tt][Rr]%s*>)', '%1' .. marker)
		if s:match(marker) then
			s = mw.ustring.gsub(s, marker .. '%s*' .. marker, '')
			s = mw.ustring.gsub(s, '([\r\n]|-[^\r\n]*[\r\n])%s*' .. marker, '%1')
			s = mw.ustring.gsub(s, marker .. '%s*([\r\n]|-)', '%1')
			s = mw.ustring.gsub(s, '(</[Cc][Aa][Pp][Tt][Ii][Oo][Nn]%s*>%s*)' .. marker, '%1')
			s = mw.ustring.gsub(s, '(<%s*[Tt][Aa][Bb][Ll][Ee][^<>]*>%s*)' .. marker, '%1')
			s = mw.ustring.gsub(s, '^(%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
			s = mw.ustring.gsub(s, '([\r\n]%{|[^\r\n]*[\r\n]%s*)' .. marker, '%1')
			s = mw.ustring.gsub(s, marker .. '(%s*</[Tt][Aa][Bb][Ll][Ee]%s*>)', '%1')
			s = mw.ustring.gsub(s, marker .. '(%s*\n|%})', '%1')
		end
		if s:match(marker) then
			local subcells = mw.text.split(s, marker)
			s = ''
			for k = 1, #subcells do
				if k == 1 then
					s = s .. subcells[k] .. '</' .. tt .. '></tr>'
				elseif k == #subcells then
					local rowstyle = ' style="display:none"'
					if notempty(subcells[k]) then rowstyle = ''	end
					s = s .. '<tr' .. rowstyle ..'><' .. tt .. ' colspan=2>\n' ..
						subcells[k]
				elseif notempty(subcells[k]) then
					if (k % 2) == 0 then
						s = s .. subcells[k]
					else
						s = s .. '<tr><' .. tt .. ' colspan=2>\n' ..
							subcells[k] .. '</' .. tt .. '></tr>'
					end
				end
			end
		end
		-- the next two lines add a newline at the end of lists for the PHP parser
		-- [[Special:Diff/849054481]]
		-- remove when [[:phab:T191516]] is fixed or OBE
		s = mw.ustring.gsub(s, '([\r\n][%*#;:][^\r\n]*)$', '%1\n')
		s = mw.ustring.gsub(s, '^([%*#;:][^\r\n]*)$', '%1\n')
		s = mw.ustring.gsub(s, '^([%*#;:])', '\n%1')
		s = mw.ustring.gsub(s, '^(%{%|)', '\n%1')
		return s
	else
		return sval
	end
end

-- Returns the union of the values of two tables, as a sequence.
local function union(t1, t2)

	local vals = {}
	for k, v in pairs(t1) do
		vals[v] = true
	end
	for k, v in pairs(t2) do
		vals[v] = true
	end
	local ret = {}
	for k, v in pairs(vals) do
		table.insert(ret, k)
	end
	return ret
end

-- Returns a table containing the numbers of the arguments that exist
-- for the specified prefix. For example, if the prefix was 'data', and
-- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
local function getArgNums(prefix)
	local nums = {}
	for k, v in pairs(args) do
		local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
		if num then table.insert(nums, tonumber(num)) end
	end
	table.sort(nums)
	return nums
end

-- Adds a row to the infobox, with either a header cell
-- or a label/data cell combination.
local function addRow(rowArgs)
	
	if rowArgs.header and rowArgs.header ~= '_BLANK_' then
		root
			:tag('tr')
				:addClass(rowArgs.rowclass)
				:cssText(rowArgs.rowstyle)
				:tag('th')
					:attr('colspan', '2')
					:addClass('infobox-header')
					:addClass(rowArgs.class)
					:addClass(args.headerclass)
					-- @deprecated next; target .infobox-<name> .infobox-header
					:cssText(args.headerstyle)
					:cssText(rowArgs.rowcellstyle)
					:wikitext(fixChildBoxes(rowArgs.header, 'th'))
		if rowArgs.data then
			root:wikitext(
				'[[Category:Pages which use infobox templates with ignored data cells]]'
			)
		end
	elseif rowArgs.data and rowArgs.data:gsub(
		category_in_empty_row_pattern, ''
		):match('^%S') then
		local row = root:tag('tr')
		row:addClass(rowArgs.rowclass)
		row:cssText(rowArgs.rowstyle)
		if rowArgs.label then
			row
				:tag('th')
					:attr('scope', 'row')
					:addClass('infobox-label')
					-- @deprecated next; target .infobox-<name> .infobox-label
					:cssText(args.labelstyle)
					:cssText(rowArgs.rowcellstyle)
					:wikitext(rowArgs.label)
					:done()
		end

		local dataCell = row:tag('td')
		dataCell
			:attr('colspan', not rowArgs.label and '2' or nil)
			:addClass(not rowArgs.label and 'infobox-full-data' or 'infobox-data')
			:addClass(rowArgs.class)
			-- @deprecated next; target .infobox-<name> .infobox(-full)-data
			:cssText(rowArgs.datastyle)
			:cssText(rowArgs.rowcellstyle)
			:wikitext(fixChildBoxes(rowArgs.data, 'td'))
	else
		table.insert(empty_row_categories, rowArgs.data or '')
	end
end

local function renderTitle()
	if not args.title then return end

	root
		:tag('caption')
			:addClass('infobox-title')
			:addClass(args.titleclass)
			-- @deprecated next; target .infobox-<name> .infobox-title
			:cssText(args.titlestyle)
			:wikitext(args.title)
end

local function renderAboveRow()
	if not args.above then return end

	root
		:tag('tr')
			:tag('th')
				:attr('colspan', '2')
				:addClass('infobox-above')
				:addClass(args.aboveclass)
				-- @deprecated next; target .infobox-<name> .infobox-above
				:cssText(args.abovestyle)
				:wikitext(fixChildBoxes(args.above,'th'))
end

local function renderBelowRow()
	if not args.below then return end

	root
		:tag('tr')
			:tag('td')
				:attr('colspan', '2')
				:addClass('infobox-below')
				:addClass(args.belowclass)
				-- @deprecated next; target .infobox-<name> .infobox-below
				:cssText(args.belowstyle)
				:wikitext(fixChildBoxes(args.below,'td'))
end

local function addSubheaderRow(subheaderArgs)
	if subheaderArgs.data and
		subheaderArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
		local row = root:tag('tr')
		row:addClass(subheaderArgs.rowclass)

		local dataCell = row:tag('td')
		dataCell
			:attr('colspan', '2')
			:addClass('infobox-subheader')
			:addClass(subheaderArgs.class)
			:cssText(subheaderArgs.datastyle)
			:cssText(subheaderArgs.rowcellstyle)
			:wikitext(fixChildBoxes(subheaderArgs.data, 'td'))
	else
		table.insert(empty_row_categories, subheaderArgs.data or '')
	end
end

local function renderSubheaders()
	if args.subheader then
		args.subheader1 = args.subheader
	end
	if args.subheaderrowclass then
		args.subheaderrowclass1 = args.subheaderrowclass
	end
	local subheadernums = getArgNums('subheader')
	for k, num in ipairs(subheadernums) do
		addSubheaderRow({
			data = args['subheader' .. tostring(num)],
			-- @deprecated next; target .infobox-<name> .infobox-subheader
			datastyle = args.subheaderstyle,
			rowcellstyle = args['subheaderstyle' .. tostring(num)],
			class = args.subheaderclass,
			rowclass = args['subheaderrowclass' .. tostring(num)]
		})
	end
end

local function addImageRow(imageArgs)

	if imageArgs.data and
		imageArgs.data:gsub(category_in_empty_row_pattern, ''):match('^%S') then
		
		local row = root:tag('tr')
		row:addClass(imageArgs.rowclass)

		local dataCell = row:tag('td')
		dataCell
			:attr('colspan', '2')
			:addClass('infobox-image')
			:addClass(imageArgs.class)
			:cssText(imageArgs.datastyle)
			:wikitext(fixChildBoxes(imageArgs.data, 'td'))
	else
		table.insert(empty_row_categories, imageArgs.data or '')
	end
end

local function renderImages()
	if args.image then
		args.image1 = args.image
	end
	if args.caption then
		args.caption1 = args.caption
	end
	local imagenums = getArgNums('image')
	for k, num in ipairs(imagenums) do
		local caption = args['caption' .. tostring(num)]
		local data = mw.html.create():wikitext(args['image' .. tostring(num)])
		if caption then
			data
				:tag('div')
					:addClass('infobox-caption')
					-- @deprecated next; target .infobox-<name> .infobox-caption
					:cssText(args.captionstyle)
					:wikitext(caption)
		end
		addImageRow({
			data = tostring(data),
			-- @deprecated next; target .infobox-<name> .infobox-image
			datastyle = args.imagestyle,
			class = args.imageclass,
			rowclass = args['imagerowclass' .. tostring(num)]
		})
	end
end

-- When autoheaders are turned on, preprocesses the rows
local function preprocessRows()
	if not args.autoheaders then return end
	
	local rownums = union(getArgNums('header'), getArgNums('data'))
	table.sort(rownums)
	local lastheader
	for k, num in ipairs(rownums) do
		if args['header' .. tostring(num)] then
			if lastheader then
				args['header' .. tostring(lastheader)] = nil
			end
			lastheader = num
		elseif args['data' .. tostring(num)] and
			args['data' .. tostring(num)]:gsub(
				category_in_empty_row_pattern, ''
			):match('^%S') then
			local data = args['data' .. tostring(num)]
			if data:gsub(category_in_empty_row_pattern, ''):match('%S') then
				lastheader = nil
			end
		end
	end
	if lastheader then
		args['header' .. tostring(lastheader)] = nil
	end
end

-- Gets the union of the header and data argument numbers,
-- and renders them all in order
local function renderRows()

	local rownums = union(getArgNums('header'), getArgNums('data'))
	table.sort(rownums)
	for k, num in ipairs(rownums) do
		addRow({
			header = args['header' .. tostring(num)],
			label = args['label' .. tostring(num)],
			data = args['data' .. tostring(num)],
			datastyle = args.datastyle,
			class = args['class' .. tostring(num)],
			rowclass = args['rowclass' .. tostring(num)],
			-- @deprecated next; target .infobox-<name> rowclass
			rowstyle = args['rowstyle' .. tostring(num)],
			rowcellstyle = args['rowcellstyle' .. tostring(num)]
		})
	end
end

local function renderNavBar()
	if not args.name then return end

	root
		:tag('tr')
			:tag('td')
				:attr('colspan', '2')
				:addClass('infobox-navbar')
				:wikitext(require('Module:Navbar')._navbar{
					args.name,
					mini = 1,
				})
end

local function renderItalicTitle()
	local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
	if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
		root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
	end
end

-- Categories in otherwise empty rows are collected in empty_row_categories.
-- This function adds them to the module output. It is not affected by
-- args.decat because this module should not prevent module-external categories
-- from rendering.
local function renderEmptyRowCategories()
	for _, s in ipairs(empty_row_categories) do
		root:wikitext(s)
	end
end

-- Render tracking categories. args.decat == turns off tracking categories.
local function renderTrackingCategories()
	if args.decat == 'yes' then return end
	if args.child == 'yes' then
		if args.title then
			root:wikitext(
				'[[Category:Pages which use embedded infobox templates with the title parameter]]'
			)
		end
	elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
		root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
	end
end

--[=[
Loads the templatestyles for the infobox.

TODO: load base templatestyles here rather than in MediaWiki:Common.css
We aren't doing it here yet because there are 4-5000 pages with 'raw' infobox
tables. See [[Mediawiki_talk:Common.css/to_do#Infobox]] and/or come help :).
When we do this we should clean up the inline CSS below too.
Will have to do some bizarre conversion category like with sidebar.
	
]=]
local function loadTemplateStyles()
	local frame = mw.getCurrentFrame()
	
-- See function description
--	local base_templatestyles = frame:extensionTag{
--		name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
--	}
	
	local templatestyles = ''
	if args['templatestyles'] then templatestyles = frame:extensionTag{
			name = 'templatestyles', args = { src = args['templatestyles'] }
		}
	end
	
	local child_templatestyles = ''
	if args['child templatestyles'] then child_templatestyles = frame:extensionTag{
			name = 'templatestyles', args = { src = args['child templatestyles'] }
		}
	end
	
	local grandchild_templatestyles = ''
	if args['grandchild templatestyles'] then grandchild_templatestyles = frame:extensionTag{
			name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
		}
	end

	return table.concat({
--		base_templatestyles, -- see function description
		templatestyles,
		child_templatestyles,
		grandchild_templatestyles
	})	
	
end

-- Specify the overall layout of the infobox, with special settings if the
-- infobox is used as a 'child' inside another infobox.
local function _infobox()
	if args.child ~= 'yes' then
		root = mw.html.create('table')

		root
			:addClass(args.subbox == 'yes' and 'infobox-subbox' or 'infobox')
			:addClass(args.bodyclass)
			-- @deprecated next; target .infobox-<name>
			:cssText(args.bodystyle)

		renderTitle()
		renderAboveRow()
	else
		root = mw.html.create()

		root
			:wikitext(args.title)
	end

	renderSubheaders()
	renderImages()
	preprocessRows()
	renderRows()
	renderBelowRow()
	renderNavBar()
	renderItalicTitle()
	renderEmptyRowCategories()
	renderTrackingCategories()

	return loadTemplateStyles() .. tostring(root)
end

-- If the argument exists and isn't blank, add it to the argument table.
-- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
local function preprocessSingleArg(argName)
	if origArgs[argName] and origArgs[argName] ~= '' then
		args[argName] = origArgs[argName]
	end
end

-- Assign the parameters with the given prefixes to the args table, in order, in
-- batches of the step size specified. This is to prevent references etc. from
-- appearing in the wrong order. The prefixTable should be an array containing
-- tables, each of which has two possible fields, a "prefix" string and a
-- "depend" table. The function always parses parameters containing the "prefix"
-- string, but only parses parameters in the "depend" table if the prefix
-- parameter is present and non-blank.
local function preprocessArgs(prefixTable, step)
	if type(prefixTable) ~= 'table' then
		error("Non-table value detected for the prefix table", 2)
	end
	if type(step) ~= 'number' then
		error("Invalid step value detected", 2)
	end

	-- Get arguments without a number suffix, and check for bad input.
	for i,v in ipairs(prefixTable) do
		if type(v) ~= 'table' or type(v.prefix) ~= "string" or
			(v.depend and type(v.depend) ~= 'table') then
			error('Invalid input detected to preprocessArgs prefix table', 2)
		end
		preprocessSingleArg(v.prefix)
		-- Only parse the depend parameter if the prefix parameter is present
		-- and not blank.
		if args[v.prefix] and v.depend then
			for j, dependValue in ipairs(v.depend) do
				if type(dependValue) ~= 'string' then
					error('Invalid "depend" parameter value detected in preprocessArgs')
				end
				preprocessSingleArg(dependValue)
			end
		end
	end

	-- Get arguments with number suffixes.
	local a = 1 -- Counter variable.
	local moreArgumentsExist = true
	while moreArgumentsExist == true do
		moreArgumentsExist = false
		for i = a, a + step - 1 do
			for j,v in ipairs(prefixTable) do
				local prefixArgName = v.prefix .. tostring(i)
				if origArgs[prefixArgName] then
					-- Do another loop if any arguments are found, even blank ones.
					moreArgumentsExist = true
					preprocessSingleArg(prefixArgName)
				end
				-- Process the depend table if the prefix argument is present
				-- and not blank, or we are processing "prefix1" and "prefix" is
				-- present and not blank, and if the depend table is present.
				if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
					for j,dependValue in ipairs(v.depend) do
						local dependArgName = dependValue .. tostring(i)
						preprocessSingleArg(dependArgName)
					end
				end
			end
		end
		a = a + step
	end
end

-- Parse the data parameters in the same order that the old {{infobox}} did, so
-- that references etc. will display in the expected places. Parameters that
-- depend on another parameter are only processed if that parameter is present,
-- to avoid phantom references appearing in article reference lists.
local function parseDataParameters()

	preprocessSingleArg('autoheaders')
	preprocessSingleArg('child')
	preprocessSingleArg('bodyclass')
	preprocessSingleArg('subbox')
	preprocessSingleArg('bodystyle')
	preprocessSingleArg('title')
	preprocessSingleArg('titleclass')
	preprocessSingleArg('titlestyle')
	preprocessSingleArg('above')
	preprocessSingleArg('aboveclass')
	preprocessSingleArg('abovestyle')
	preprocessArgs({
		{prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
	}, 10)
	preprocessSingleArg('subheaderstyle')
	preprocessSingleArg('subheaderclass')
	preprocessArgs({
		{prefix = 'image', depend = {'caption', 'imagerowclass'}}
	}, 10)
	preprocessSingleArg('captionstyle')
	preprocessSingleArg('imagestyle')
	preprocessSingleArg('imageclass')
	preprocessArgs({
		{prefix = 'header'},
		{prefix = 'data', depend = {'label'}},
		{prefix = 'rowclass'},
		{prefix = 'rowstyle'},
		{prefix = 'rowcellstyle'},
		{prefix = 'class'}
	}, 50)
	preprocessSingleArg('headerclass')
	preprocessSingleArg('headerstyle')
	preprocessSingleArg('labelstyle')
	preprocessSingleArg('datastyle')
	preprocessSingleArg('below')
	preprocessSingleArg('belowclass')
	preprocessSingleArg('belowstyle')
	preprocessSingleArg('name')
	-- different behaviour for italics if blank or absent
	args['italic title'] = origArgs['italic title']
	preprocessSingleArg('decat')
	preprocessSingleArg('templatestyles')
	preprocessSingleArg('child templatestyles')
	preprocessSingleArg('grandchild templatestyles')
end

-- If called via #invoke, use the args passed into the invoking template.
-- Otherwise, for testing purposes, assume args are being passed directly in.
function p.infobox(frame)
	if frame == mw.getCurrentFrame() then
		origArgs = frame:getParent().args
	else
		origArgs = frame
	end
	
	parseDataParameters()
	
	return _infobox()
end

-- For calling via #invoke within a template
function p.infoboxTemplate(frame)
	origArgs = {}
	for k,v in pairs(frame.args) do origArgs[k] = mw.text.trim(v) end
	
	parseDataParameters()
	
	return _infobox()
end
return p