Documentation for this module may be created at ሞድዩል:Infobox/doc

--
-- This module implements {{Infobox}}
--
 
local p = {}
 
local HtmlBuilder = require('Module:HtmlBuilder')
local BadgesCategorization = require('Module:Badges categorization')
local frame = {}
local args
local root
local widthImage = '320px'
function union(t1, t2)
    -- Returns the union of the values of two tables, as a sequence.
    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

local function debugEmpty(content)
    if content and content ~= '' then
        return content
    end
end

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 = {}
    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

local function addrow(rowArgs)
    -- Adds a row to the infobox, with either a header cell
    -- or a label/data cell combination.
    if rowArgs.section then
        root
            .tag('tr')
                .tag('th')
                    .attr('colspan', 3)
                    .addClass(rowArgs.rowclass)
                    .css('text-align', 'center')
                    .cssText(rowArgs.sectionstyle or args.sectionstyle)
                    .wikitext(rowArgs.section)
    elseif rowArgs.data then
        local row = root.tag('tr')
        row.addClass(rowArgs.rowclass)
        if rowArgs.label then
            row
                .tag('th')
                    .attr('scope', 'row')
                    .css('text-align', 'left')
                    .addClass(rowArgs.rowclass)
                    .cssText(rowArgs.labelstyle or args.labelstyle)
                    .wikitext(rowArgs.label)
                    .done()
        end
        
        local dataCell = row.tag('td')
        if rowArgs.label then 
            dataCell
                .attr('colspan', 2)
        else
            dataCell
                .attr('colspan', 3)
                .css('text-align', 'center') 
        end
        dataCell
            .addClass(rowArgs.class)
            .cssText(rowArgs.datastyle or args.datastyle)
            
               .wikitext('\n' .. rowArgs.data)
            --.newline()	
    end
end

local function renderTitle()
    if not args.title then return end
local header = {}    
    if args.media == 'yes' and args.classtitle then
      header = 'media ' .. args['classtitle']
    elseif args.media == 'yes' then
      header = 'media ' 
    elseif args.headertype then
      header = 'header ' .. args.headertype
    elseif args.classtitle then
      header = 'header ' .. args.classtitle
    else 
      header = 'header '
    end
    root
        .tag('tr')
           .tag('th')
              .attr('colspan', 3)
              .addClass(header)
              .css('text-align', 'center')
              .css('background-color', args.backgroundcolor or args.colorbackgroundtitle or 'transparent')
              .css('color', args.textcolor or 'black')
              .cssText(args.titlestyle)
              .wikitext(args.title)
end

local function renderAboveRow()
    if not args.above and not args.title2 then return end
    
    root
        .tag('tr')
            .tag('th')
                .attr('colspan', 3)
                .addClass(args.aboveclass or args.titleclass2)
                .css('text-align', 'center')
                .css('font-size', '125%')
                .css('font-weight', 'bold')
                .cssText(args.abovestyle)
                .wikitext(args.above)
end

local function renderTableFooter()-- Table footer, it will appear at the bottom of the table
    if not args.tablefooter then return end
    
    root
        .tag('tr')
            .tag('td')
                .attr('colspan', '3')
                .addClass(args.tablefooterclass)
                .css('text-align', 'center')
                .cssText(args.tablefooterstyle)
                .wikitext(args.tablefooter)
end
local function renderBottomImage()-- Image that will appear at the bottom of the table
    if not args.bottomimage then return end
            bottomimage  = {}
            if args['bottomimagesize']  == nil or args['bottomimagesize'] == '' then
                 args['bottomimagesize'] = widthImage
            end
            if string.find(args.bottomimage, '[{[]') == nil then
            bottomimage = ('[[ፋይል:' .. args.bottomimage .. '|'.. args['bottomimagesize'] .. ']]' )
            else 
            bottomimage = args.bottomimage
            end
    
    root
        .tag('tr')
            .tag('td')
                .attr('colspan', '3')
                .addClass('image ' .. (args.bottomimageclass or '') )
                .css('text-align', 'center')
                .cssText(args.bottomimagestyle)
                .newline()
                .wikitext(bottomimage)
                .tag('br', {selfClosing = true})
                    .done()
                .tag('div')
                    .css('display','inline')
                    .cssText(args.bottomfooterstyle)
                    .wikitext(args.bottomfooter)
                    .done()
                    .newline()
end

local function renderAboveImage()-- Image that will appear at the top of the table
    if not args.aboveimage then return end
    if args['aboveimagesize']  == nil or args['aboveimagesize']  == '' then
            args['aboveimagesize'] = widthImage
    end
    aboveimage  = {}
            if string.find(args.aboveimage, '[{[]') == nil then
            aboveimage = ('[[ፋይል:' .. args.aboveimage .. '|'.. args['aboveimagesize'] .. ']]' )
            else 
            aboveimage = args.aboveimage
            end
    
    root
        .tag('tr')
            .tag('td')
                .attr('colspan', '3')
                .addClass('image ' .. (args.aboveimageclass or '') )
                .css('text-align', 'center')
                .cssText(args.aboveimagestyle)
                .newline()
                .wikitext(aboveimage)
                .tag('br', {selfClosing = true})
                    .done()
                .tag('div')
                    .css('display','inline')
                    .cssText(args.abovefooterstyle)
                    .wikitext(args.abovefooter)
                    .done()
                    .newline()
end

local function renderSubtitles()-- Subtitles of the infobox
    if args.subtitle then
        args.subtitle1 = args.subtitle
    end
    if args.subtitleclass then
        args.subtitleclass1 = args.subtitleclass
    end
    local subtitlenumber = getArgNums('subtitle')
    for k, num in ipairs(subtitlenumber) do
        addrow({
            data      = args['subtitle' .. num],
            datastyle = args['subtitlestyle' .. num] or args.subtitlestyle,
            class     = args.subtitleclass,
            rowclass  = args['subtitleclass' .. num]
        })
    end
end
local function renderaboverows()-- rows above side images 
    if args.abovedata then
        args.abovedata1 = args.abovedata
    end
    if args.abovedataclass then
        args.abovedataclass1 = args.abovedataclass
    end

    if args.abovedatastyle then
        args.abovedatastyle1 = args.abovedatastyle
    end
    local abovedatanumber = getArgNums('abovedata')
    for k, num in ipairs(abovedatanumber) do
        addrow({
            data = args['abovedata' .. num],
            datastyle = args['abovedatastyle' .. num],
            class = args.abovedataclass,
            rowclass = args['abovedataclass' .. num]
        })
    end
end
local function renderSideimages()
-- Images that will appear above in a geminate way for example shields and flags
   
if args['leftimagesize'] == "" or args['leftimagesize'] == nil then
         args['leftimagesize'] = '100px'
      end 
      if args['rightimagesize'] == "" or args['rightimagesize'] == nil then
         args['rightimagesize'] = '100px'
      end
    if args.rightimage and args.leftimage then
      if args.leftfooter then leftbrconditional = 'br' end
      if args.rightfooter then rightbrconditional = 'br' end
      	
      root
        .tag('tr')
        .tag('td')
        --.attr('cellspacing', '0em')
        --.attr('padding','0em')
        .attr('colspan', '3')
        .css('align', 'center')
        .tag('table') -- it has to go inside a table so that the rows don't deform it
        .css('width', '100%')
        .addClass('mergedrow')
        .tag('tr')
            .tag('td')
            .css('text-align', 'center')
                .css('background-color', 'transparent')
                .addClass(args.leftimageclass)
                .css('align', 'center')-- It aligns in the horizontal center
                .css('text-align', 'center') -- It aligns in the horizontal center
                .css('vertical-align', 'middle')-- It aligns in the vertical center
                .cssText(args.leftimagestyle)
                .wikitext('[[ፋይል:' .. args.leftimage .. '|' .. args['leftimagesize'] .. ']]' )
                .tag(leftbrconditional)
                .tag('div')
                    .css('display','inline')
                    .cssText(args.leftfooterstyle)
                    .wikitext(args.leftfooter)
                    .done()
            .tag('td')
                .css('text-align', 'center')-- It aligns in the horizontal center
                .css('align', 'center')-- It aligns in the horizontal center
                .css('vertical-align', 'middle')-- It aligns in the vertical center
                .css('background-color', 'transparent')
                .addClass(args.rightimageclass)
                .cssText(args.rightimagestyle)       
                .wikitext('[[ፋይል:' .. args.rightimage .. '|' .. args['rightimagesize'] .. ']]' )
                .tag(rightbrconditional)
                .tag('div')
                    .css('display','inline')
                    .cssText(args.rightfootersyle)
                    .wikitext(args.rightfooter)
                    .done()
                .newline()
     elseif args.rightimage or args.leftimage then 
-- If only one of the two, the image that appears will be in the center
           imageS = {}    
           if args.rightimage ~= '' and  args.rightimage ~= nil then
               imageS = 'rightimage'  
           elseif args.leftimage ~= '' and  args.leftimage ~= nil then
               imageS = 'leftimage'
           end
        footerS = {}
          if args.rightimage then
               footerS = 'rightfooter' 
          elseif args.leftimage then
               footerS = 'leftfooter' 
          end
      root
        .tag('tr')
            .tag('td')
                .attr('colspan', '3')
                .addClass(args['class' .. imageS])
                .css('text-align', 'center')
                .cssText(args['style' .. imageS])
                .newline()
                .wikitext('[[ፋይል:' ..  args[imageS] .. '|'.. args['size'..imageS] .. ']]' )
                .tag('br')
                .tag('div')
                   .css('display','inline')
                   .cssText(args['style' .. footerS])
                   .wikitext(args[footerS])
                   .done()
                
    end
    end
            
local function renderImages() -- Can create infinite number of images 
    if args.image then
        args.image1 = args.image
    end
     if args['imagesize'] then
        args['imagesize1'] = args['imagesize']
    end

    if args.footer then
        args.footer1 = args.footer
    end
    local imagenums = getArgNums('image')

    for k, num in ipairs(imagenums) do
        local footer = args['footer' .. num]
        local floating = args['floatingimage' .. num] or false
            if args['imagesize'..num]  == nil then
                args['imagesize'..num]  =    widthImage
            end
        image  = {}
            local searchString = mw.ustring.gsub(args['image'..num],'UNIQ','[') -- So that this does not cause problems with certain templates
            if mw.ustring.find(searchString, '[{[|]') == nil then -- Check if there is [ or { to not add prefix
            image = ('[[ፋይል:' .. args['image' .. num] .. '|'.. args['imagesize' ..num] .. ']]' )
            else 
            image = args['image'..num]
            end
        local data = HtmlBuilder.create().wikitext(image)
        if footer and not floating then
        	data
                .tag('br', {selfClosing = true})
                    .done()
        end
        if footer then
            data
                .tag('div')
                    .css('display','inline')
                    .cssText(args.footerstyle)
                    .wikitext(footer)
                    .done()
        end
        addrow({
            data = tostring(data),
            datastyle = args.imagestyle,
            class = args.imageclass,
            rowclass = args['imageclass' .. num]
        })
    end
end

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('section'), getArgNums('data'))
    table.sort(rownums)
    for k, num in ipairs(rownums) do
        addrow({
        	subtitlestyle = debugEmpty(args['subtitlestyle' .. num]),
            section      = debugEmpty(args['section' .. num]),
            sectionstyle = debugEmpty(args['sectionstyle' .. num]),
            label        = debugEmpty(args['label' .. num]),
            data         = debugEmpty(args['data' .. num]),
            labelstyle   = debugEmpty(args['labelstyle' .. num]),
            datastyle    = debugEmpty(args['datastyle' .. num]),
            class        = debugEmpty(args['class' .. num]),
            rowclass     = debugEmpty(args['rowclass' .. num])
        })
    end
end

function hasDataRow(row)
    -- Function that returns true if the row or group of rows (in the case of
    -- sections) has data.
    
    if row.kind == 'section' then
        for k, rowSection in ipairs(row) do
            if hasDataRow(rowSection) then 
                return true
            end
        end
    elseif row.kind == 'succession' then
        if debugEmpty(row[1]) or debugEmpty(row['last']) or
           debugEmpty(row[3]) or debugEmpty(row['Next']) then 
            return true 
        end
    else
        if debugEmpty(row[2]) or debugEmpty(row['data']) then
            return true
        end
    end
    
    return false
end

function addSuccession(successionArguments)
    local row = root.tag('tr')
    row.css('font-size', '88%')
    row.css('text-align', 'center')
    
    local cell
    local width
    
    width = '33%'
    
    cell = row.tag('td')
    cell
            .css('width', width)
            .css('padding', '0.2em 0.1em 0.2em 0')
            .css('vertical-align', 'middle')

    if successionArguments['font style'] then
           cell
                .tag('div')
                    .css('display','inline')
                    .css('font-style', successionArguments['font style'])
                    .wikitext(successionArguments.last)
                    .done()
    else 
        cell.wikitext(successionArguments.last)
    end
        
    if successionArguments['last year'] then
        cell
            .tag('br')
            .wikitext('(' .. successionArguments['last year'] .. ')')
    end
    
    cell = row.tag('td')
    cell
         .css('width', width)    
            .css('padding', '0.2em 0.1em')
            .css('vertical-align', 'middle')
            .css('background-color', successionArguments.color or '#E6E8FA')
            
    cell
         .tag('div')
              .css('display','inline')
              .css('font-weight', 'bold')
              .css('font-style', successionArguments['font style'] or '')
              .wikitext(successionArguments.current or args.title)
              .done()
              
    if successionArguments['year'] then
        cell
            .tag('br')
            .wikitext('(' .. successionArguments['year'] .. ')')
    end              
    
    cell = row.tag('td')
    cell
            .css('width', width)
            .css('padding', '0.2em 0 0.2em 0.1em')
            .css('vertical-align', 'middle')
        
    if successionArguments['font style'] then
           cell
                .tag('div')
                    .css('display','inline')
                    .css('font-style', successionArguments['font style'])
                    .wikitext(successionArguments.Next)
                    .done()
    else 
        cell.wikitext(successionArguments.Next)
    end
        
    if successionArguments['next year'] then
        cell
            .tag('br')
            .wikitext('(' .. successionArguments['next year'] .. ')')
    end
end
function renderTableRows(board)
    -- Function that makes up the rows of a table, either the file or a section of it.
    
    local addedSectionTitle = false
    
    for k, row in ipairs(table) do
        if hasDataRow(row) then
            if row.kind == 'section' then
                -- Add the title of the section (if you are informed)
                local sectiontitle = debugEmpty(row.title) or debugEmpty(row['title'])
                if sectiontitle then
                    addrow({
                        sectionstyle = row['titlestyle'],
                        section      = sectiontitle
                   })
                end
                renderTableRows(board)
            elseif row.kind == 'dropdown section' then -- MISSING
            elseif row.kind == 'succession' then
                addSuccession({
                    ['last']   = debugEmpty(row[1]) or debugEmpty(row['last']),
                    ['current']    = debugEmpty(row['current']),
                    ['Next']  = debugEmpty(row[3]) or debugEmpty(row['Next']),
                    ['last year']  = debugEmpty(row['last year']),
                    ['year']       = debugEmpty(row['year']),
                    ['next year']  = debugEmpty(row['next year']),
                    ['font style'] = debugEmpty(row['font style']),
                    ['color']      = debugEmpty(row['color'])
                })
            elseif row.kind == 'two columns'  then -- MISSING
            elseif row.kind == 'three columns' then -- MISSING          
            else                      -- Label + Data or just Data
                addrow({
                    label       = debugEmpty(row[1]) or debugEmpty(row['label']),
                    data        = debugEmpty(row[2]) or debugEmpty(row['data']),
                    labelstyle  = row['labelstyle'] or board['labelstyle'],
                    datastyle   = row['datastyle']    or board['datastyle'],
                    class       = row['class']          or board['class'],
                    rowclass    = row['rowclass']      or board['rowclass']
                })
            end        
        end
    end   
end

local function renderNavigationBar()-- Create a link to the template that is given with a name at the bottom
    if not args.name then return end
    
    root
        .tag('tr')
            .tag('td')
                .attr('colspan', '3')
                .css('text-align', 'right')
                .wikitext(mw.getCurrentFrame():expandTemplate({ 
                    title = 'navbar', 
                    args = { args.name, mini = 1 }
                }))
end

local function renderBarWikidata()-- Create a link to the Wikidata item at the bottom
   
    local pageLink = mw.title.getCurrentTitle().prefixedText 
    local pageLabel = mw.ustring.gsub(pageLink,'%s%(.*%)','') 
    local entity = args.entity or mw.wikibase.getEntityIdForCurrentPage()
    local footerText = ""
    if  (args.child == 'yes' or args.integrated == 'yes' or args.wikidata == 'no') or (mw.title.getCurrentTitle().namespace ~= 0 and mw.title.getCurrentTitle().namespace ~= 104 and not args.entity) then
        footerText = ''
    elseif  entity ~= "" and entity ~= nil  then
        footerText = '<div class="plainlinks wikidata-link" style="font-size: 0.85em">&#x5B;[[d:' .. tostring(entity) .. '|ኣብ ዊኪዳታ ሓበሬታ ኣርትዕ]]&#x5D;</div>'
        if (entity ~= mw.wikibase.getEntityIdForCurrentPage()) and (mw.title.getCurrentTitle().namespace == 0 or mw.title.getCurrentTitle().namespace == 104)  then
        	footerText = footerText .. '[[መደብ:ዊኪፐድያ:Articles with infoboxes that use arbitrary access]]'
        end
    else
        footerText = "<small>'''Page not linked to [[Wikidata]]'''\n"..
"* If it does not exist in other Wikipedias: [<span class=plainlinks>[//www.wikidata.org/w/index.php?title=Special:NewItem&site=eswiki&page="..mw.uri.encode(pageLink,WIKI) .."&label="..mw.uri.encode(pageLabel,WIKI) .." create&nbsp;new&nbsp;item]]</span>\n"..
"* If it exists in other Wikipedias: [<span class=plainlinks>[[:d:Special:ItemByTitle|search&nbsp;ítem&nbsp;to&nbsp;link]]</span>]\n"..
"and add the link in Tigrinya: ".. pageLink ..".</small>"
    end
    if footerText ~= '' then
    	root
        	.tag('tr')
	            .tag('td')
	                .addClass('noprint')
                	.attr('colspan', '3')
                	.css('text-align', 'left')
                	.wikitext(footerText)
    end
end
local function renderTrackingCategories()
    if args.decat ~= 'yes' and #(getArgNums('data')) == 0 and not args[1] and mw.title.getCurrentTitle().namespace == 0 then
        root.wikitext('[[መደብ:ዊኪፐድያ:Articles using infobox without data in rows]]')
    end
    
    if BadgesCategorization.hasAnyBadge() == '1' then
	    root.wikitext(BadgesCategorization.badgesCategories())
	end
end

function _infobox()
	if args.child ~= 'yes' and args.integrated ~= 'yes' then
        root = HtmlBuilder.create('table')
        
        root   -- Style of the entire infobox
            .addClass('infobox')
            .addClass(args.class)
            .cssText('width:22.7em; line-height: 1.4em; text-align:left; padding:.23em') -- Same as template:Infobox
            .cssText(args.style)
            if args.style and (mw.title.getCurrentTitle().namespace == 10) then -- So that it only adds it in the template namespace
            	root.wikitext('[[መደብ:ዊኪፐድያ:Infoboxes with the style parameter]]')
        end
        renderTitle()
        renderAboveRow()
    else
        root = HtmlBuilder.create()
        
        if args.title then
            root.wikitext("'''" .. args.title .. "'''")
        end
    end

    renderSubtitles()
    renderAboveImage()
    renderSideimages()
    renderaboverows()
    renderImages() 
    if not args[1] then
        renderRows()
    else
        renderTableRows(args)
    end
    renderBottomImage()
    renderTableFooter()
    renderNavigationBar()
    renderBarWikidata()
    renderTrackingCategories()
    
    return mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Template:Infobox/imagelimit.css' } } .. tostring(root)
end
local function touchParameters(prefixTable, origArgs, step)
    -- 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.
    if type(prefixTable) ~= 'table' or type(origArgs) ~= 'table' then
    	error("Invalid input detected for the touchParameters function. Both parameters must be tables.", 2)
    end
    if step and type(step) ~= 'number' then
    	 error("Invalid step value detected", 2)
    end
    
    step = step or 20
    local temp
    local a = 1 
    local moreArgumentsExist = true
    for j,v in ipairs(prefixTable) do
        if not type(v) == "string" then
            error("Non-string value detected in table prefix by touchParameters function.", 2)
        end
        temp = origArgs[v]
     end
    while moreArgumentsExist == true do
        moreArgumentsExist = false
        for i = a, a + step - 1 do
            for j,v in ipairs(prefixTable) do
                temp = origArgs[v .. tostring(i)]
                if temp then
                    moreArgumentsExist = true
                end
            end
        end
        a = a + step
    end
end 
function p.infobox(frame)
	local origArgs
	frame = frame
    -- If called via #invoke, use the args passed into the invoking template.
    -- Otherwise, for testing purposes, assume args are being passed directly in.
    if frame == mw.getCurrentFrame() then
        origArgs = frame:getParent().args
    else
        origArgs = frame
    end
 -- Parse the data parameters in the same order that the old {{infobox}} did, so that
 -- references etc. will display in the expected places.
    local temp
    temp = origArgs.title
    temp = origArgs.above
    touchParameters({'subtitle'}, origArgs, 5)
    touchParameters({'image', 'footer'}, origArgs, 5)
    touchParameters({'section', 'label', 'data'}, origArgs, 20)
    temp = origArgs.tablefooter
    
    -- The function parser considers an empty string to be false, so to preserve the previous
    -- behavior of {{Infobox}}, you must change the empty arguments to zero, so Lua will consider them
    -- which are false too (except for 'title italic' parameters, which specifies different behavior
    -- depending on whether it is absent or empty)
    args = {}
    for k, v in pairs(origArgs) do
        if v ~= ''  then
            args[k] = v
        end
    end
	
	return _infobox()
end

return p