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:BattleHonour/sandbox

From Wikipedia, the free encyclopedia
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