Edge Rewrite
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