Edge Rewrite
// HTMLRewriter · presentation

This page was redesigned at the edge.

Cloudflare fetched the original article and streamed it through HTMLRewriter to apply an entirely new visual system without rebuilding the source page.

Jump to content

Module:MilAward

Permanently protected module
From Wikipedia, the free encyclopedia

require('strict')
local p = {}
local data = mw.loadData('Module:MilAward/data')
local getArgs = require('Module:Arguments').getArgs

local function countByCountry()
    -- Counts how many award entries exist per country.
    local StatCount = {}
    for code, record in pairs(data) do
        local country = record.Country
        StatCount[country] = (StatCount[country] or 0) + 1
    end
    return StatCount
end

function p.GraphData(frame)
    -- Produces the x= and y= lines for a graph of number of entries per country.
    local StatCount = countByCountry()
    local xValues, yValues = {}, {}
    for country, count in pairs(StatCount) do
        table.insert(xValues, country)
        table.insert(yValues, count)
    end
    local output = " \n| x=" .. table.concat(xValues, ", ") .. " \n| y=" .. table.concat(yValues, ", ") .. "\n"
    return output
end

function p.Stats(frame)
    local StatCount = countByCountry()

    -- Stable alphabetical order so the country table is readable before the reader sorts it.
    local countries = {}
    for country, _ in pairs(StatCount) do
        table.insert(countries, country)
    end
    table.sort(countries)

    local wikiRows = {}
    for _, country in ipairs(countries) do
        table.insert(wikiRows, "|-\n|" .. country .. "||" .. StatCount[country])
    end

    -- Do not append GraphData-style | x= / | y= here: those were leftover from the old
    -- Graph:Chart workflow and rendered as broken text after the wikitable.
    local wikiCode = "{| class=\"wikitable sortable col2right\"\n|-\n! Country !! Count\n" .. table.concat(wikiRows, "\n") .. "\n|}"

    local tablist = p.PrintStats()
    local output = wikiCode .. "\n\n" .. tablist
    return output
end

function p.PrintStats()
    -- Convert key-value pairs into a list of {code, data[code]} pairs
    local pairsList = {}
    for code, values in pairs(data) do
        table.insert(pairsList, {code, values})
    end

    -- Sort the list based on the "Description" field of each pair (guard against a missing field)
    table.sort(pairsList, function(a, b)
        return (a[2].Description or "") < (b[2].Description or "")
    end)

    -- Generate wiki table code for the sorted list
    local tableCode = "{| class=\"wikitable sortable\"\n"
    tableCode = tableCode .. "|-\n"
    tableCode = tableCode .. "! Code !! Old Code !! Description !! Country !! Postnom !! Ribbon !! Org\n"

    for i, pair in ipairs(pairsList) do
        local code, values = pair[1], pair[2]
        local oldcode = values.Code or ""
        local country = values.Country or ""
        local description = values.Description or ""
        local class = values.Class or ""
        local postnom = values.PostNom or ""
        local ribbonimage = values.RibbonImage or ""
        local pagelink = values.PageLink or ""
        local note = values.Note or ""
        local org = values.Org or ""

        tableCode = tableCode .. "|-\n"
        tableCode = tableCode .. "|" .. code .. "\n"
        if code == oldcode then
        	tableCode = tableCode .. "| \n"
        else
        	tableCode = tableCode .. "|" .. oldcode .. "\n"
        end
        tableCode = tableCode .. "|[[" .. pagelink .. "|" .. description
        if string.len(class) > 0 then
        	tableCode = tableCode .. "]] '''(" .. class .. ")'''"
		else
        	tableCode = tableCode .. "]]"
        end
        if string.len(note) > 0 then
            tableCode = tableCode .. mw.getCurrentFrame():expandTemplate{ title = 'Efn', args = {note} } -- Note: Page needs {{notelist}}
        end
        tableCode = tableCode .. "\n"
        tableCode = tableCode .. "|" .. country .. "\n"
        tableCode = tableCode .. "|" .. postnom .. "\n"
        tableCode = tableCode .. "|[[File:" .. ribbonimage .. "|x30px]]\n"
        tableCode = tableCode .. "|" .. org .. "\n"
    end

    tableCode = tableCode .. "|}"
    return tableCode
end

function p.Display(frame)
	-- Take parameters out of the frame and pass them to p._Display(). Return the result.
	local templateArgs = getArgs(frame)
	local code = templateArgs[1] or 'None'
	local size = templateArgs[2] or "40px"
	local namedcode = templateArgs["code"]
	if namedcode then
		code = namedcode
	end
	local namedsize = templateArgs["size"]
	if namedsize then
		size = namedsize
	end
	return p._Display(code, size)
end

function p._Display(code, size)
    -- Debug/inspection helper: dumps the raw data fields for a code. (size is currently unused.)
    local rec = data[code]
    if not rec then
        return '<span style="color:#d33"> (Code not found: <b>' .. tostring(code) .. '</b>)</span> [[Category:MilAward Error]]'
    end
    local output = ""
    output = output .. " '''Found award with code''': " .. rec.Code .. "<br />"
    output = output .. "''Code'': " .. rec.Code .. "<br />"
    output = output .. "''Description'': " .. rec.Description .. "<br />"
    output = output .. "''Ribbon Image'': " .. rec.RibbonImage .. "<br />"
    output = output .. "''Post-Nominal'': " .. rec.PostNom .. "<br />"
    output = output .. "''Page Link'': " .. rec.PageLink .. "<br />"
    output = output .. "''Country'': " .. rec.Country .. "<br />"
    return output
end

local function _Ribbon(code, size)
	local rec = data[code]
	if string.len(size) < 1 then
		size = "x12px" -- Check that there is a value for size and if not, set the value
	end
	local ribbonimage = rec.RibbonImage or ""
	if string.len(ribbonimage) < 1 then -- If there is no ribbonimage defined for the record, then assign a default one.
		ribbonimage = "NOT-AVAILABLE-RIBBON.svg"
	end
    local output = "[[File:" .. ribbonimage .. "|".. size .. "|link=" .. rec.PageLink .. "|" .. rec.Description .. " ''" .. rec.PostNom .. " ''" .. "]]"
    return output
end

function p.Ribbon(frame)
	-- Function extracts the parameters from the frame (passed into the function)
	-- It checks that the first argument is not empty, and returns an error if so
	local templateArgs = getArgs(frame)
	local code = templateArgs[1] or 'None'
	local size = templateArgs[2] or "x12px"

    local namedcode = templateArgs["code"] -- Check for named argument "code" and overwrite the value in the local variable if it has a value
	if namedcode then
		code = namedcode
	end

	local namedsize = templateArgs["size"] -- Check for named argument "size" and overwrite the value in the local variable if it has a value
	if namedsize then
		size = namedsize
	end

	if string.match(code, "%*") then -- The code field has asterisks in it. Raise an error
		local output = '<span style="color:#d33"> Bad Code: <b>[' .. code .. ']</b>. Replace all * with x</span> [[Category:MilAward Error]]'
		return output
	end -- End if

	if not code or '' == code then
		return '<span style="color:#d33"> (Invalid Code: <b>' .. code .. '</b> )</span> [[Category:MilAward Error]]'
	end

	-- Code is case sensitive - do not uppercase it
	if not data[code] then
		return '<span style="color:#d33"> (Code not found: <b>' .. code .. '</b> )</span> [[Category:MilAward Error]]'
	end
	return _Ribbon (code, size)
end


local function _RibbonDesc(code, size, country, norib, nocat, org)
    -- This routine is where the data is structured for output
    local rec = data[code]
    local output = ''
    if norib ~= "yes" then -- They do NOT want the ribbon to display if this is yes
    	output = output .. _Ribbon(code, size) -- Start by getting the ribbon. Call the function designed to do that
    end
    output = output ..  " [[" .. rec.PageLink .. "|" .. rec.Description .. "]] "  -- add the pagelink and description
    if rec.Class and string.len(rec.Class) > 0 then -- Only add the Class if there is something in the field
    	output = output .. " (''" .. rec.Class .. "'') "
    end
    if string.len(rec.PostNom or "") > 0 then -- Only add the postnom if there is something in the field
    	 output = output .. " ('''" .. rec.PostNom .. "''') "
    end

    if string.len(rec.Note or "") > 0 then -- Only add the Note if there is something in the field
    	output = output .. mw.getCurrentFrame():expandTemplate{ title = 'Efn', args = {rec.Note} } -- Note: Page needs {{notelist}}
    end
    if string.len(country) > 0 then -- if a value for country has been specified
        if country:upper() ~= (rec.Country or ""):upper() then -- check that the country specified does NOT equal that on record
            output = output .. " (" .. rec.Country .. ") " -- add the country code to the end of the output
        end
    end
    if string.len(org) > 0 then -- if a value for org has been specified
        if org:upper() == "YES" then -- check that it is YES
            if string.len(rec.Org or "") > 0 then -- Check there is actually something in the org field
                output = output .. " (''" .. rec.Org .. "'') " -- add the Org code to the end of the output
            end
        end
    end

    local nocat_length = string.len(nocat)
    if nocat_length > 1 then -- If ANYTHING has been specified as a value for nocat, then it's true
        -- Caller opted out of recipient categorisation
    else
        -- Need to exclude adding the recipient category if it is not in mainspace.
        local current_title = mw.title.getCurrentTitle()
        if current_title.namespace == 0 then -- mainspace only
            local recipcat = rec.RecipCat or "" -- If there is a recipient category specified, then add it to the output
            local categories = {}
            if string.len(recipcat) > 0 then
                local categoryTitle = mw.title.new('Category:' .. mw.text.trim(recipcat))
                if categoryTitle.exists and not categoryTitle.isRedirect then
                    -- category exists and is not a redirect
                    categories[#categories+1] = " [[Category:" .. recipcat .. "]]"
                end
                -- else: category does not exist or is a redirect - not added
            end
            output = output .. table.concat(categories)
        end
    end

    return output
end

function p.RibbonDesc(frame)
	local templateArgs = getArgs(frame)
    local code = 'Unknown' -- initialise a local variable called code to something rational
    local size = 'x20px' -- initialise a local variable called size to something rational
	if templateArgs[1] then code = templateArgs[1] end -- check that it is not "nil" before assigning the value
    if templateArgs[2] then size = templateArgs[2] end -- Check that it is not "nil" before assigning the value

	local namedcode = templateArgs["code"] -- assign the local variable "namedcode" to the value of the argument called code
	if namedcode then -- if the assignment of value has worked ie there was actually a parameter called code with a value then
		code = namedcode -- assign the value found above to the variable "code" overwriting any other value it may have had
	end

	local namedsize = templateArgs["size"] -- exactly the same as for "code" above
	if namedsize then
		size = namedsize
	end

    local country = templateArgs["country"] or '' -- if the argument "country" has been passed, assign it to a local variable
    local norib = templateArgs["norib"] or ''

	if not code or '' == code then -- if there was no code assigned (field empty) then return an error of invalid code
		local output =  '<span style="color:#d33"> (Invalid Code: <b>' .. code .. '</b>)</span>'
        return output
	end
	if string.match(code, "%*") then -- The code field has asterisks in it. Raise an error
		local output = '<span style="color:#d33"> Bad Code: <b>[' .. code .. ']</b>. Replace all * with x</span> [[Category:MilAward Error]]'
		return output
	end -- End if

	-- Do not convert to uppercase. Codes are case sensitive
	if not data[code] then
		return '<span style="color:#d33"> (Code: <b>' .. code .. '</b> Not found)</span>'
	end

    local nocat = templateArgs["nocat"] or '' -- Assign local value for nocat parameter
    local org = templateArgs["org"] or '' -- Assign local value for org parameter

	return _RibbonDesc (code, size, country, norib, nocat, org)
end

function p.Stack(frame)
    local templateArgs = getArgs(frame)
    local size = templateArgs["size"] or "x12px"

    local RibbonList = {}
    for i = 1, 40 do -- 40 is the max number of params that will be examined.
        local rl = templateArgs[i] or ""
        if rl ~= "" then
            table.insert(RibbonList, rl)
        end
    end
    local row = {}
    for i, rl in ipairs(RibbonList) do
        if data[rl] then -- check whether this is found in the data
            table.insert(row, _Ribbon(rl, size))
        end
    end
    return table.concat(row, " ")
end

function p.DescList(frame)
    local templateArgs = getArgs(frame)
    local size = templateArgs["size"] or 'x20px'
    local namedcountry = templateArgs["country"] or 'yes'
    local output = '\n'

    for i = 1, 40 do -- 40 is the max number of positional params examined, matching p.Stack
        local value = templateArgs[i]
        if value and string.len(value) > 1 and data[value] then
            output = output .. "* " .. _RibbonDesc(value, size, namedcountry, '', '', '') .. "\n"
        end
    end
    return output
end

function p.PostNom(frame)
    local templateArgs = getArgs(frame)
    local size = templateArgs["size"] or '85%'
    local sep = templateArgs["sep"] or ', '
    if sep == "none" then
        sep = " "
    end
    local output = '<span class="noexcerpt nowraplinks" style="font-size:' .. size .. '; font-weight:normal;">'
    local any = false

    for i = 1, 40 do -- positional args only, so this both preserves order and skips size/sep automatically
        local value = templateArgs[i]
        if value and string.len(value) > 1 and data[value] then
            if string.len(data[value].PostNom or "") > 1 then
                output = output .. "[[" .. data[value].PageLink .. "|" ..  data[value].PostNom .. "]]" .. sep
                any = true
            end
        end
    end
    if any then
        local seplen = (string.len(sep) + 1) * -1 -- Take length of sep, add one to it and make it negative (* -1)
        output = output:sub(1, seplen) -- Remove the trailing separator
    end
    output = output .. '</span>'

    return output
end

-- Initial code by John Dovey (13 April 2023) [[User:BoonDock]], 
-- Updated with bug fixes 7 September 2026
-- Updated 10 September 2026: Stats no longer dumps GraphData | x=/| y= after the country wikitable; country rows sorted A–Z
-- - PostNom no longer corrupts its <span> tag when nothing matches 
-- - DescList/PostNom iterate args positionally (like Stack already did) so award order matches what the editor typed, and named params can't collide with codes
-- - PrintStats header is back on one line (was silently misaligning the table)
-- - _Display guards against unknown codes instead of throwing a script error
-- - Leading-comma bug in the GraphData/Stats CSV lines fixed, and the duplicated country-counting loop is now one shared countByCountry() helper
-- - _Ribbon/_RibbonDesc cache data[code] once instead of re-indexing repeatedly, and guard Country/PostNom/Note/Org/RibbonImage against missing fields (same nil-crash class as the Description sort bug)
-- - Mainspace check now uses current_title.namespace == 0 instead of the namespace-name-length heuristic
-- - Dead code removed: unreachable if not size check, stray commented-out lines, unused module-level GlobalTemplateArgs, redundant local wikiCode shadow
-- x -- 
return p