Editing Module:Infobox

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. Read the Privacy Policy to learn what information we collect about you and how we use it.

If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 2: Line 2:
-- This module implements {{Infobox}}
-- This module implements {{Infobox}}
--
--
 
local p = {}
local p = {}
 
local navbar = require('Module:Navbar')._navbar
local HtmlBuilder = require('Module:HtmlBuilder')
 
local args = {}
local args
local origArgs
local root
local root
 
   
local function notempty( s ) return s and s:match( '%S' ) end
function union(t1, t2)
 
     -- return the union of the values of two tables, as a sequence
local function fixChildBoxes(sval, tt)
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
-- https://en.wikipedia.org/w/index.php?title=Template_talk:Infobox_musical_artist&oldid=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')
return s
else
return sval
end
end
 
local function union(t1, t2)
     -- Returns the union of the values of two tables, as a sequence.
     local vals = {}
     local vals = {}
     for k, v in pairs(t1) do
     for k, v in pairs(t1) do
Line 77: Line 27:


local function getArgNums(prefix)
local function getArgNums(prefix)
    -- 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 nums = {}
     local nums = {}
     for k, v in pairs(args) do
     for k, v in pairs(args) do
         local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
         local num = ('' .. k):match('^' .. prefix .. '([1-9]%d*)$')
         if num then table.insert(nums, tonumber(num)) end
         if num then table.insert(nums, tonumber(num)) end
     end
     end
Line 90: Line 37:


local function addRow(rowArgs)
local function addRow(rowArgs)
    -- Adds a row to the infobox, with either a header cell
    -- or a label/data cell combination.
     if rowArgs.header then
     if rowArgs.header then
         root
         root
             :tag('tr')
             .tag('tr')
                 :addClass(rowArgs.rowclass)
                 .tag('th')
                :cssText(rowArgs.rowstyle)
                     .attr('colspan', 2)
                :attr('id', rowArgs.rowid)
                     .addClass(rowArgs.class)
                :tag('th')
                     .css('text-align', 'center')
                     :attr('colspan', 2)
                     .cssText(args.headerstyle)
                     :attr('id', rowArgs.headerid)
                     .wikitext(rowArgs.header)
                    :addClass(rowArgs.class)
                     :addClass(args.headerclass)
                    :css('text-align', 'center')
                     :cssText(args.headerstyle)
                     :cssText(rowArgs.rowcellstyle)
                    :wikitext(fixChildBoxes(rowArgs.header, 'th'))
     elseif rowArgs.data then
     elseif rowArgs.data then
         local row = root:tag('tr')
         local row = root.tag('tr')
         row:addClass(rowArgs.rowclass)
         row.addClass(rowArgs.rowclass)
        row:cssText(rowArgs.rowstyle)
        row:attr('id', rowArgs.rowid)
         if rowArgs.label then
         if rowArgs.label then
             row
             row
                 :tag('th')
                 .tag('th')
                     :attr('scope', 'row')
                     .attr('scope', 'row')
                     :attr('id', rowArgs.labelid)
                     .css('text-align', 'left')
                     :cssText(args.labelstyle)
                     .cssText(args.labelstyle)
                     :cssText(rowArgs.rowcellstyle)
                     .wikitext(rowArgs.label)
                    :wikitext(rowArgs.label)
                     .done()
                     :done()
         end
         end
          
          
         local dataCell = row:tag('td')
         local dataCell = row.tag('td')
         if not rowArgs.label then  
         if not rowArgs.label then  
             dataCell
             dataCell
                 :attr('colspan', 2)
                 .attr('colspan', 2)
                 :css('text-align', 'center')  
                 .css('text-align', 'center')  
         end
         end
         dataCell
         dataCell
             :attr('id', rowArgs.dataid)
             .addClass(rowArgs.class)
            :addClass(rowArgs.class)
             .cssText(rowArgs.datastyle)
             :cssText(rowArgs.datastyle)
             .wikitext(rowArgs.data)
             :cssText(rowArgs.rowcellstyle)
            :newline()
            :wikitext(fixChildBoxes(rowArgs.data, 'td'))
     end
     end
end
end
Line 143: Line 76:


     root
     root
         :tag('caption')
         .tag('caption')
             :addClass(args.titleclass)
             .addClass(args.titleclass)
             :cssText(args.titlestyle)
             .cssText(args.titlestyle)
             :wikitext(args.title)
             .wikitext(args.title)
end
end


Line 153: Line 86:
      
      
     root
     root
         :tag('tr')
         .tag('tr')
             :tag('th')
             .tag('th')
                 :attr('colspan', 2)
                 .attr('colspan', 2)
                 :addClass(args.aboveclass)
                 .addClass(args.aboveclass)
                 :css('text-align', 'center')
                 .css('text-align', 'center')
                 :css('font-size', '125%')
                 .css('font-size', '125%')
                 :css('font-weight', 'bold')
                 .css('font-weight', 'bold')
                 :cssText(args.abovestyle)
                 .cssText(args.abovestyle)
                 :wikitext(fixChildBoxes(args.above,'th'))
                 .wikitext(args.above)
end
end


Line 168: Line 101:
      
      
     root
     root
         :tag('tr')
         .tag('tr')
             :tag('td')
             .tag('td')
                 :attr('colspan', '2')
                 .attr('colspan', '2')
                 :addClass(args.belowclass)
                 .addClass(args.belowclass)
                 :css('text-align', 'center')
                 .css('text-align', 'center')
                 :cssText(args.belowstyle)
                 .cssText(args.belowstyle)
                 :newline()
                 .newline()
                 :wikitext(fixChildBoxes(args.below,'td'))
                 .wikitext(args.below)
                .newline()
end
end


Line 188: Line 122:
     for k, num in ipairs(subheadernums) do
     for k, num in ipairs(subheadernums) do
         addRow({
         addRow({
             data = args['subheader' .. tostring(num)],
             data = args['subheader' .. num],
             datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
             datastyle = args.subheaderstyle or args['subheaderstyle' .. num],
             class = args.subheaderclass,
             class = args.subheaderclass,
             rowclass = args['subheaderrowclass' .. tostring(num)]
             rowclass = args['subheaderrowclass' .. num]
         })
         })
     end
     end
Line 205: Line 139:
     local imagenums = getArgNums('image')
     local imagenums = getArgNums('image')
     for k, num in ipairs(imagenums) do
     for k, num in ipairs(imagenums) do
         local caption = args['caption' .. tostring(num)]
         local caption = args['caption' .. num]
         local data = mw.html.create():wikitext(args['image' .. tostring(num)])
         local data = HtmlBuilder.create().wikitext(args['image' .. num])
         if caption then
         if caption then
             data
             data
                 :tag('div')
                 .tag('br', {selfClosing = true})
                     :cssText(args.captionstyle)
                     .done()
                     :wikitext(caption)
                .tag('span')
                    .cssText(args.captionstyle)
                     .wikitext(caption)
         end
         end
         addRow({
         addRow({
Line 217: Line 153:
             datastyle = args.imagestyle,
             datastyle = args.imagestyle,
             class = args.imageclass,
             class = args.imageclass,
             rowclass = args['imagerowclass' .. tostring(num)]
             rowclass = args['imagerowclass' .. num]
         })
         })
     end
     end
Line 223: Line 159:


local function renderRows()
local function renderRows()
    -- Gets the union of the header and data argument numbers,
    -- and renders them all in order using addRow.
     local rownums = union(getArgNums('header'), getArgNums('data'))
     local rownums = union(getArgNums('header'), getArgNums('data'))
     table.sort(rownums)
     table.sort(rownums)
     for k, num in ipairs(rownums) do
     for k, num in ipairs(rownums) do
         addRow({
         addRow({
             header = args['header' .. tostring(num)],
             header = args['header' .. num],
             label = args['label' .. tostring(num)],
             label = args['label' .. num],
             data = args['data' .. tostring(num)],
             data = args['data' .. num],
             datastyle = args.datastyle,
             datastyle = args.datastyle,
             class = args['class' .. tostring(num)],
             class = args['class' .. num],
             rowclass = args['rowclass' .. tostring(num)],
             rowclass = args['rowclass' .. num]
            rowstyle = args['rowstyle' .. tostring(num)],
            rowcellstyle = args['rowcellstyle' .. tostring(num)],
            dataid = args['dataid' .. tostring(num)],
            labelid = args['labelid' .. tostring(num)],
            headerid = args['headerid' .. tostring(num)],
            rowid = args['rowid' .. tostring(num)]
         })
         })
     end
     end
Line 249: Line 177:
      
      
     root
     root
         :tag('tr')
         .tag('tr')
             :tag('td')
             .tag('td')
                 :attr('colspan', '2')
                 .attr('colspan', '2')
                 :css('text-align', 'right')
                 .css('text-align', 'right')
                 :wikitext(navbar{
                 .wikitext(mw.getCurrentFrame():expandTemplate({
                     args.name,
                    title = 'navbar',
                    mini = 1,
                     args = { args.name, mini = 1 }
                 })
                 }))
end
end


Line 262: Line 190:
     local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
     local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
     if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
     if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
         root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
         root.wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
     end
     end
end
end
Line 268: Line 196:
local function renderTrackingCategories()
local function renderTrackingCategories()
     if args.decat ~= 'yes' then
     if args.decat ~= 'yes' then
    if args.child == 'yes' then
        if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
        if args.title then
            root.wikitext('[[Category:Articles which use infobox templates with no data rows]]')
            root:wikitext('[[Category:Pages which use embedded infobox templates with the title parameter]]')
        end
        end
         if args.child == 'yes' and args.title then
         elseif #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
             root.wikitext('[[Category:Articles which use embedded infobox templates with the title parameter]]')
             root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
         end
         end
     end
     end
Line 279: Line 206:


local function _infobox()
local function _infobox()
    -- Specify the overall layout of the infobox, with special settings
    -- if the infobox is used as a 'child' inside another infobox.
     if args.child ~= 'yes' then
     if args.child ~= 'yes' then
         root = mw.html.create('table')
         root = HtmlBuilder.create('table')
          
          
         root
         root
             :addClass((args.subbox ~= 'yes') and 'infobox' or nil)
             .addClass('infobox')
             :addClass(args.bodyclass)
            .addClass(args.bodyclass)
            .attr('cellspacing', 3)
            .css('border-spacing', '3px')
             .cssText(args.bodystyle)
              
              
             if args.subbox == 'yes' then
             if args.subbox == 'yes' then
                 root
                 root
                     :css('padding', '0')
                     .css('padding', '0')
                     :css('border', 'none')
                     .css('border', 'none')
                     :css('margin', '-3px')
                     .css('margin', '-3px')
                     :css('width', 'auto')
                     .css('width', 'auto')
                     :css('min-width', '100%')
                     .css('min-width', '100%')
                     :css('font-size', '100%')
                     .css('font-size', '100%')
                     :css('clear', 'none')
                     .css('clear', 'none')
                     :css('float', 'none')
                     .css('float', 'none')
                     :css('background-color', 'transparent')
                     .css('background-color', 'transparent')
             else
             else
                 root
                 root
                     :css('width', '22em')
                     .css('width', '22em')
             end
             end
        root
            :cssText(args.bodystyle)
      
      
         renderTitle()
         renderTitle()
         renderAboveRow()
         renderAboveRow()
     else
     else
         root = mw.html.create()
         root = HtmlBuilder.create()
       
        root
            :wikitext(args.title)
     end
     end


Line 326: Line 249:
end
end


local function preprocessSingleArg(argName)
-- This function parses the parameters with the given prefixes, in order, in batches of 20.
    -- If the argument exists and isn't blank, add it to the argument table.
local function touchParameters(prefixTable, origArgs)
    -- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
     if type(prefixTable) ~= 'table' or type(origArgs) ~= 'table' then
     if origArgs[argName] and origArgs[argName] ~= '' then
         error("Invalid input to the touchParameters function detected. Both parameters must be tables.", 2)
         args[argName] = origArgs[argName]
     end
     end
end
    local temp
 
     local a = 1
local function preprocessArgs(prefixTable, step)
     local moreArgumentsExist = true
     -- Assign the parameters with the given prefixes to the args table, in order, in batches
     for j,v in ipairs(prefixTable) do
    -- of the step size specified. This is to prevent references etc. from appearing in the
         if not type(v) == "string" then
    -- wrong order. The prefixTable should be an array containing tables, each of which has
             error("Non-string value detected in the prefix table in the touchParameters function.", 2)
    -- 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.
    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
        temp = origArgs[v]
     end
     end
    -- Get arguments with number suffixes.
    local a = 1 -- Counter variable.
    local moreArgumentsExist = true
     while moreArgumentsExist == true do
     while moreArgumentsExist == true do
         moreArgumentsExist = false
         moreArgumentsExist = false
         for i = a, a + step - 1 do
         for i = a, a+19 do
             for j,v in ipairs(prefixTable) do
             for j,v in ipairs(prefixTable) do
                 local prefixArgName = v.prefix .. tostring(i)
                 temp = origArgs[v .. tostring(i)]
                 if origArgs[prefixArgName] then
                 if temp then
                     moreArgumentsExist = true -- 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
             end
         end
         end
         a = a + step
         a = a + 20
     end
     end
end
end
   
   
function p.infobox(frame)
function p.infobox(frame)
    local origArgs
     -- If called via #invoke, use the args passed into the invoking template.
     -- If called via #invoke, use the args passed into the invoking template.
     -- Otherwise, for testing purposes, assume args are being passed directly in.
     -- Otherwise, for testing purposes, assume args are being passed directly in.
Line 402: Line 288:
      
      
     -- Parse the data parameters in the same order that the old {{infobox}} did, so that
     -- 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
     -- references etc. will display in the expected places.
     -- another parameter are only processed if that parameter is present, to avoid
     local temp
     -- phantom references appearing in article reference lists.
     temp = origArgs.title
    preprocessSingleArg('child')
     temp = origArgs.above
    preprocessSingleArg('bodyclass')
     touchParameters({'subheader'}, origArgs)
    preprocessSingleArg('subbox')
     touchParameters({'image', 'caption'}, origArgs)
    preprocessSingleArg('bodystyle')
     touchParameters({'header', 'label', 'data'}, origArgs)
    preprocessSingleArg('title')
    temp = origArgs.below
     preprocessSingleArg('titleclass')
    preprocessSingleArg('titlestyle')
    -- ParserFunctions considers whitespace to be false, so to preserve the previous
    preprocessSingleArg('above')
    -- behavior of {{infobox}}, change any whitespace arguments to nil, so Lua will consider
    preprocessSingleArg('aboveclass')
     -- them false too. (Except the 'italic title' param, which specifies different behavior
     preprocessSingleArg('abovestyle')
     -- depending on whether it's absent or empty)
    preprocessArgs({
     args = {}
        {prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
     for k, v in pairs(origArgs) do
    }, 10)
        if mw.ustring.match(v, '%S') or k == 'italic title' then
     preprocessSingleArg('subheaderstyle')
            args[k] = v
    preprocessSingleArg('subheaderclass')
        end
    preprocessArgs({
     end
        {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'},
        {prefix = 'dataid'},
        {prefix = 'labelid'},
        {prefix = 'headerid'},
        {prefix = 'rowid'}
    }, 50)
     preprocessSingleArg('headerclass')
     preprocessSingleArg('headerstyle')
     preprocessSingleArg('labelstyle')
     preprocessSingleArg('datastyle')
    preprocessSingleArg('below')
    preprocessSingleArg('belowclass')
    preprocessSingleArg('belowstyle')
    preprocessSingleArg('name')
    args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
     preprocessSingleArg('decat')
   
   
     return _infobox()
     return _infobox()
Please note that all contributions to Nonbinary Wiki are considered to be released under the Creative Commons Attribution-ShareAlike (see Nonbinary Wiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)

Template used on this page: