Module:BattleHonour/sandbox
Appearance
| This is the module sandbox page for Module:BattleHonour (diff). |
require('strict')
local p = {}
local data = mw.loadData('Module:BattleHonour/data')
local getArgs = require('Module:Arguments').getArgs
local TRACKING_UNKNOWN = '[[Category:Pages with unknown BattleHonour codes]]'
local PERIOD_ORDER = {
'Colonial', 'AngloBoer', 'WW1_SWA', 'WW1_EA', 'WW1_Western', 'WW1_ME',
'WW2_EA', 'WW2_Madagascar', 'WW2_WesternDesert', 'WW2_Italy', 'BorderWar', 'Other',
}
local PERIOD_LABELS = {
Colonial = 'Colonial and pre-Union campaigns',
AngloBoer = 'Second Boer War',
WW1_SWA = 'First World War — South West Africa',
WW1_EA = 'First World War — East Africa',
WW1_Western = 'First World War — Western Front',
WW1_ME = 'First World War — Egypt and Palestine',
WW2_EA = 'Second World War — East Africa',
WW2_Madagascar = 'Second World War — Madagascar',
WW2_WesternDesert = 'Second World War — Western Desert',
WW2_Italy = 'Second World War — Italy',
BorderWar = 'Border War and southern Africa',
Other = 'Other',
}
local function trim(s)
if not s then
return ''
end
return mw.text.trim(tostring(s))
end
local function resolve(code)
code = trim(code)
if code == '' then
return nil, code
end
local entry = data[code]
if entry then
return entry, code
end
return nil, code
end
local function honourFileWikitext(entry, imgsize, linked)
local image = entry.Image or ''
local name = entry.Name or ''
local link = entry.Link or ''
if not linked or link == '' then
return string.format('[[File:%s|border|%s|%s]]', image, imgsize, name)
end
return string.format('[[File:%s|border|%s|link=%s|%s]]', image, imgsize, link, name)
end
local function collectCodes(args)
local codes = {}
local i = 1
while true do
local v = args[i]
if v == nil then
break
end
v = trim(v)
if v ~= '' then
table.insert(codes, v)
end
i = i + 1
if i > 500 then
break
end
end
if args.list then
codes = mw.text.split(args.list, '%s*,%s*')
for j, c in ipairs(codes) do
codes[j] = trim(c)
end
end
return codes
end
local function sortedEntries(includeAliases)
local list = {}
for code, entry in pairs(data) do
if type(entry) == 'table' and entry.Name then
local skipAlias = false
if not includeAliases then
if code == 'SWA15' and data.South_West_Africa_1915 then
skipAlias = true
elseif code == 'SWA14_15' and data.South_West_Africa_1914_1915 then
skipAlias = true
end
end
if not skipAlias then
table.insert(list, { code = code, entry = entry })
end
end
end
table.sort(list, function(a, b)
local sa = a.entry.SortKey or '999999'
local sb = b.entry.SortKey or '999999'
if sa == sb then
return (a.entry.Name or a.code) < (b.entry.Name or b.code)
end
return sa < sb
end)
return list
end
function p.BHTable(frame)
local args = getArgs(frame)
local classes = { 'wikitable' }
if args.float then
table.insert(classes, args.float)
end
local caption = args.caption or 'Battle Honours'
local title = args.Title or 'Awarded'
local imgsize = args.size or 'x10px'
local border = args.borderColor or '#CF9C65'
local codes = collectCodes(args)
if args.unlinked then
return table.concat(codes, ', ')
end
local root = mw.html.create('table')
root:addClass(table.concat(classes, ' '))
root:cssText(string.format(
'margin:0.5em auto; font-size:95%%; border-bottom:5px solid %s; border-top:5px solid %s; width:50%%;',
border, border
))
root:tag('caption'):wikitext(caption)
-- One honour per row (vertical list). The pre-2026 module accidentally
-- stacked images this way by injecting "|-" inside cells; keep that layout.
local headerRow = root:tag('tr')
headerRow:tag('th')
:cssText('background:lightgrey; text-align:center;')
:wikitext(title)
local unknown = {}
if #codes == 0 then
root:tag('tr'):tag('td')
:cssText('text-align:center;')
:wikitext("''No battle honours specified''")
else
for _, code in ipairs(codes) do
local entry, resolved = resolve(code)
local cell = root:tag('tr'):tag('td'):cssText('text-align:center;')
if not entry then
cell:wikitext("''Unknown code:'' '''" .. resolved .. "'''")
table.insert(unknown, resolved)
else
cell:wikitext(honourFileWikitext(entry, imgsize, true))
end
end
end
local out = tostring(root)
if #unknown > 0 then
out = out .. TRACKING_UNKNOWN
end
return out
end
function p.catalogue(frame)
local list = sortedEntries(false)
local root = mw.html.create('table')
root:addClass('wikitable sortable')
local hr = root:tag('tr')
hr:tag('th'):wikitext('Code')
hr:tag('th'):wikitext('Name')
hr:tag('th'):wikitext('Image')
hr:tag('th'):wikitext('Period')
hr:tag('th'):wikitext('Description')
for _, item in ipairs(list) do
local e = item.entry
local tr = root:tag('tr')
tr:tag('td'):tag('code'):wikitext(item.code)
local nameCell = tr:tag('td')
local anchor = e.Name or item.code
nameCell:wikitext('[[#' .. anchor .. '|' .. (e.Name or item.code) .. ']]')
local img = tr:tag('td')
-- Width-based size: height-only (xNNpx) makes wide embroidered scrolls huge.
local link = e.Link or ''
if link ~= '' then
img:wikitext(string.format(
'[[File:%s|frameless|80px|link=%s|alt=%s]]',
e.Image or '', link, e.Name or ''
))
else
img:wikitext(string.format(
'[[File:%s|frameless|80px|alt=%s]]',
e.Image or '', e.Name or ''
))
end
tr:tag('td'):wikitext(PERIOD_LABELS[e.Period] or e.Period or '')
tr:tag('td'):wikitext(e.Description or '')
end
return tostring(root)
end
local function renderUnits(entry)
local units = entry.Units
-- mw.loadData tables do not support the length operator (#); use [1].
if type(units) ~= 'table' or units[1] == nil then
return "''No recipient units listed in module data.''"
end
local bullets = {}
local i = 1
while units[i] ~= nil do
table.insert(bullets, '* ' .. units[i])
i = i + 1
end
return table.concat(bullets, '\n')
end
local function honourBlock(item)
local e = item.entry
local name = e.Name or item.code
local parts = {}
table.insert(parts, '=== ' .. name .. ' ===')
-- Plain wikitext only (no {{main}}): nested templates from #invoke are unreliable for readers/caches.
if e.Link and e.Link ~= '' and not mw.ustring.find(e.Link, 'List of South African Battle Honours', 1, true) then
table.insert(parts, "''Main article:'' [[" .. e.Link .. "]]")
end
-- frameless + width (not height): embroidered scrolls are very wide; keep in a layout table.
local file = string.format(
'[[File:%s|frameless|120px|alt=%s]]',
e.Image or '', name
)
table.insert(parts, '{|')
table.insert(parts, '|-')
table.insert(parts, '| style="vertical-align:top; width:130px" | ' .. file)
local desc = "'''" .. name .. "''' — " .. (e.Description or '')
if e.Note and e.Note ~= '' then
desc = desc .. "<br />''Note:'' " .. e.Note
end
table.insert(parts, '| style="vertical-align:top" | ' .. desc)
table.insert(parts, '|}')
table.insert(parts, '')
table.insert(parts, '==== Recipient units ====')
table.insert(parts, renderUnits(e))
table.insert(parts, '')
table.insert(parts, '<small>Module code: <code>' .. item.code .. '</code></small>')
return table.concat(parts, '\n')
end
function p.honour(frame)
local args = getArgs(frame)
local code = trim(args[1] or args.code or '')
local entry, resolved = resolve(code)
if not entry then
return '<strong class="error">Unknown battle honour code: '
.. resolved .. '</strong>' .. TRACKING_UNKNOWN
end
return honourBlock({ code = resolved, entry = entry })
end
function p.honoursByPeriod(frame)
local list = sortedEntries(false)
local byPeriod = {}
for _, item in ipairs(list) do
local period = item.entry.Period or 'Other'
byPeriod[period] = byPeriod[period] or {}
table.insert(byPeriod[period], item)
end
local out = {}
for _, period in ipairs(PERIOD_ORDER) do
local items = byPeriod[period]
if items and #items > 0 then
table.insert(out, '== ' .. (PERIOD_LABELS[period] or period) .. ' ==')
for _, item in ipairs(items) do
table.insert(out, honourBlock(item))
end
end
end
return table.concat(out, '\n')
end
function p.BattleHonourTable(frame)
return p.catalogue(frame)
end
function p.GetBattleHonour(BHCode, imgsize)
local entry = data[BHCode]
imgsize = imgsize or 'x20px'
if not entry then
return "''Unknown:'' '''" .. BHCode .. "'''"
end
return honourFileWikitext(entry, imgsize, true)
end
return p
-- Overhaul by [[User:BoonDock]] (2026); initial code April 2023