Modul:Diagram

Aus Terraria Wiki
Zur Navigation springen Zur Suche springen
Siehe auch die englische Modulseite: Module:Diagram. Sie enthält möglicherweise umfassendere oder aktuellere Informationen.

Dieses Modul stellt die Funktionalität von {{diagramm}}. Diese Vorlage dient hauptsächlich dazu, Herstellungsbäume im Wiki darzustellen. Das Modul erstellt eine Tabelle, deren Zellränder die Linien im Diagramm erzeugen und so den Effekt eines Baumes hervorrufen. Hinweise zur Verwendung der Vorlage sind auf der Vorlagenseite, {{diagramm}}, zu finden.

Die Idee basiert auf der Vorlage „Family tree“ im The Witcher Wiki. Westgrass hat sie grundlegend optimiert und die Funktionalität in dieses Modul überführt.


--------------------------------------------------------------------------------
--
-- =============================================================================
--
-- Module:Diagram
--
-- Rendering a family tree-like diagram
--
-- =============================================================================
--
-- Code annotations:
-- This module is documented according to LuaCATS (Lua Comment and Type System).
-- LuaCATS comments are prefixed with three dashes (---) and use Markdown syntax.
-- For a full list of annotations, see the following link:
-- https://luals.github.io/wiki/annotations/
--
--------------------------------------------------------------------------------


local trim = mw.text.trim

---Holds the arguments from the template call.
---@type table<string, string>
local inputArgs

---Return a trimmed version of the value of the template parameter with the specified `key`.
---Return `nil` if the parameter is empty or unset.
---@param key string|integer
---@return string?
local function getArg(key)
	local value = inputArgs[key]
	if not value then
		return nil
	end
	value = trim(value)
	if value == '' then
		return nil
	end
	return value
end

---Combine all the buffered cells during this row.
---@param row1 string Buffer from the iteration
---@param row2 string Buffer from the iteration
---@return string row HTML output for this row
local function processRow(row1, row2)
	return '<tr class="x"> ' .. row1 .. '</tr><tr class="y">' .. row2 .. '</tr>'
end

---Merge the common styles for all boxes (from the `boxstyle` argument) with the
---styles for this current box.
---@param commonBoxstyle string
---@param arg string Current box name
---@return string
local function mergeStyles(commonBoxstyle, arg)
	return table.concat({
		commonBoxstyle,
		getArg(arg .. '_boxstyle') or getArg(arg .. '_style') or getArg(arg .. '_css')
	}, ';')
end

---Table cells for connectors, i.e. horizontal and vertical lines, line corners,
---crossings, etc.
local connectorCells = {
	['_'] = {
		row1 = '<td class="l nr nb"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['-'] = {
		row1 = '<td colspan="2" class="l sb"></td>',
		row2 = '<td class="l nr"></td><td class="l"></td>',
	},
	['I'] = {
		row1 = '<td rowspan="2" class="l sr"></td><td class="l nb"></td>',
		row2 = '<td class="l"></td>',
	},
	[','] = {
		row1 = '<td class="l cf"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['v'] = {
		row1 = '<td colspan="2" class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['.'] = {
		row1 = '<td class="l sb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	[')'] = {
		row1 = '<td rowspan="2" class="l sr"></td><td class="l sb"></td>',
		row2 = '<td class="l"></td>',
	},
	['+'] = {
		row1 = '<td class="l sr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['('] = {
		row1 = '<td class="l sb sr"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['`'] = {
		row1 = '<td class="l sr"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['^'] = {
		row1 = '<td class="l sr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	["'"] = {
		row1 = '<td class="l sr sb"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['~'] = {
		row1 = '<td colspan="2" class="l db"></td>',
		row2 = '<td class="l nr"></td><td class="l"></td>',
	},
	[':'] = {
		row1 = '<td rowspan="2" class="l dr"></td><td class="l nb"></td>',
		row2 = '<td class="l"></td>',
	},
	['F'] = {
		row1 = '<td class="l cf"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['V'] = {
		row1 = '<td colspan="2" class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['7'] = {
		row1 = '<td class="l db"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['D'] = {
		row1 = '<td rowspan="2" class="l dr"></td><td class="l db"></td>',
		row2 = '<td class="l"></td>',
	},
	['X'] = {
		row1 = '<td class="l dr db"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['C'] = {
		row1 = '<td class="l db dr"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['L'] = {
		row1 = '<td class="l dr"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['A'] = {
		row1 = '<td class="l dr db"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['J'] = {
		row1 = '<td class="l dr db"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['r'] = {
		row1 = '<td class="l cf"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['y'] = {
		row1 = '<td colspan="2" class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['n'] = {
		row1 = '<td class="l db"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	[']'] = {
		row1 = '<td rowspan="2" class="l sr"></td><td class="l db"></td>',
		row2 = '<td class="l"></td>',
	},
	['$'] = {
		row1 = '<td class="l sr db"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['['] = {
		row1 = '<td class="l db sr"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['c'] = {
		row1 = '<td class="l sr"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['h'] = {
		row1 = '<td class="l sr db"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['j'] = {
		row1 = '<td class="l sr db"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['p'] = {
		row1 = '<td class="l cf"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['u'] = {
		row1 = '<td colspan="2" class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['q'] = {
		row1 = '<td class="l sb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['E'] = {
		row1 = '<td rowspan="2" class="l dr"></td><td class="l sb"></td>',
		row2 = '<td class="l"></td>',
	},
	['x'] = {
		row1 = '<td class="l dr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['3'] = {
		row1 = '<td class="l sb dr"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['b'] = {
		row1 = '<td class="l dr"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['t'] = {
		row1 = '<td class="l dr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['d'] = {
		row1 = '<td class="l dr sb"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['/'] = {
		row1 = '<td class="l dr db"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['Y'] = {
		row1 = '<td class="l dr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['\\'] = {
		row1 = '<td class="l dr sb"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['B'] = {
		row1 = '<td class="l sr db"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['G'] = {
		row1 = '<td class="l sr sb"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['R'] = {
		row1 = '<td class="l sr db"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['U'] = {
		row1 = '<td class="l sr sb"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['4'] = {
		row1 = '<td class="l sr sb"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['!'] = {
		row1 = '<td class="l sr nb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['i'] = {
		row1 = '<td class="l dr nb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['<'] = {
		row1 = '<td class="l sb nr"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['>'] = {
		row1 = '<td class="l db nr"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['9'] = {
		row1 = '<td class="l sr nb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l"></td>',
	},
	['6'] = {
		row1 = '<td colspan="2" class="l nb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['&'] = {
		row1 = '<td class="l sb nr"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['?'] = {
		row1 = '<td class="l nr"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['"'] = {
		row1 = '<td class="l dr nb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l"></td>',
	},
	[';'] = {
		row1 = '<td colspan="2" class="l nb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['a'] = {
		row1 = '<td class="l db nr"></td><td class="l"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['e'] = {
		row1 = '<td class="l nr"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['M'] = {
		row1 = '<td class="l dr db"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['W'] = {
		row1 = '<td class="l sr db"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['2'] = {
		row1 = '<td class="l dr sb"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['5'] = {
		row1 = '<td class="l dr db"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['f'] = {
		row1 = '<td class="l sr nb"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['g'] = {
		row1 = '<td class="l sr db"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['k'] = {
		row1 = '<td class="l dr nb"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['s'] = {
		row1 = '<td class="l dr db"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['o'] = {
		row1 = '<td class="l nr sb"></td><td class="l db"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['m'] = {
		row1 = '<td class="l nr db"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['w'] = {
		row1 = '<td class="l dr sb"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['z'] = {
		row1 = '<td class="l dr db"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['P'] = {
		row1 = '<td class="l dr nb"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['H'] = {
		row1 = '<td class="l dr sb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['K'] = {
		row1 = '<td class="l sr nb"></td><td class="l sb"></td>',
		row2 = '<td class="l dr"></td><td class="l"></td>',
	},
	['N'] = {
		row1 = '<td class="l sr sb"></td><td class="l" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['T'] = {
		row1 = '<td class="l nr db"></td><td class="l sb"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['Z'] = {
		row1 = '<td class="l nr sb"></td><td class="l db"></td>',
		row2 = '<td class="l sr"></td><td class="l"></td>',
	},
	['S'] = {
		row1 = '<td class="l sr db"></td><td class="l sb"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['Q'] = {
		row1 = '<td class="l sr sb"></td><td class="l db"></td>',
		row2 = '<td class="l" colspan="2"></td>',
	},
	['←'] = {
		row1 = '<td class="l sb alt"></td><td class="l sb"></td>',
		row2 = '<td class="l alb" colspan="2"></td>',
	},
	['→'] = {
		row1 = '<td class="l sb"></td><td class="l sb art"></td>',
		row2 = '<td class="l arb" colspan="2"></td>',
	},
	['↑'] = {
		row1 = '<td class="l sr atl"></td><td class="l atr" rowspan="2"></td>',
		row2 = '<td class="l sr"></td>',
	},
	['↓'] = {
		row1 = '<td class="l sr"></td><td class="l abr" rowspan="2"></td>',
		row2 = '<td class="l sr abl"></td>',
	},
	['⇠'] = {
		row1 = '<td class="l db alt"></td><td class="l db"></td>',
		row2 = '<td class="l alb" colspan="2"></td>',
	},
	['⇢'] = {
		row1 = '<td class="l db"></td><td class="l db art"></td>',
		row2 = '<td class="l arb" colspan="2"></td>',
	},
	['⇡'] = {
		row1 = '<td class="l dr atl"></td><td class="l atr" rowspan="2"></td>',
		row2 = '<td class="l dr"></td>',
	},
	['⇣'] = {
		row1 = '<td class="l dr"></td><td class="l abr" rowspan="2"></td>',
		row2 = '<td class="l dr abl"></td>',
	},
	['*'] = {  -- unlinked crossing
		row1 = '<td class="l sb ct"></td><td class="l sb"></td>',
		row2 = '<td class="l sr cb"></td><td class="l"></td>',
	},
}
-- aliases
connectorCells['0'] = connectorCells['_']
connectorCells['O'] = connectorCells['_']

--------------------------------------------------------------------------------
---Main return object
local p = {}

---Process the arguments to render a HTML `<table>` element.
---@param frame table Interface to the parser (`mw.frame`)
---@return string
p.render = function(frame)
	inputArgs = frame:getParent().args  -- global input args cache

	local boxclass = getArg('boxclass')
	local boxstyle = getArg('boxstyle') or ''

	-- The unnamed arguments come in rows, e.g.:
	-- |A|_|B|#
	-- |C|^|D|#
	-- Unnamed args = { 'A', '_', 'B', '#', 'C', '^', 'D', '#' }
	-- We will iterate over these arguments in rows, which means: Create the table
	-- cell HTML for each argument and store it in a buffer. When we reach the
	-- end of a row, i.e. a '#' argument, then combine the buffered cells, add
	-- them to the processedRows, and clear the buffer for the next row.

	---HTML table rows
	local processedRows = {}
	---Argument index during iteration
	local argIndex = 1
	---Argument value during iteration
	local arg

	-- Each row of arguments will actually consist of *two* table rows in
	-- the HTML output (one `<tr class="x">`, one `<tr class="y">`). So the buffer
	-- consists of two variables, `row1` and `row2`.

	---Row buffer during iteration (children of `<tr class="x">`).
	local row1 = ''
	---Row buffer during iteration (children of `<tr class="y">`).
	local row2 = ''

	-- Start iterating
	while true do
		arg = inputArgs[argIndex]  -- current unnamed argument
		if not arg then
			break
		end
		argIndex = argIndex+1
		arg = trim(arg)

		-- `arg` is one of these:
		-- * a row terminator, then complete the current row and start a new one
		-- * a connector (line drawing) or box (content), then create its HTML
		--   and insert it into the buffer
		-- * a blank string (argument consisting only of whitespace), then ignore it

		if arg == '#' then
			-- row terminator
			processedRows[#processedRows + 1] = processRow(row1, row2)
			-- clear the buffer for the next row
			row1 = ''
			row2 = ''
		elseif connectorCells[arg] then
			-- connector
			row1 = row1 .. connectorCells[arg].row1
			row2 = row2 .. connectorCells[arg].row2
		elseif arg == '' then
			-- blank string, skipped
		else
			-- content box
			local td = mw.html.create('td')
				:attr({
					class = 'diagram-box',
					colspan = 2 * tonumber(getArg(arg .. '_cols') or 3),
					rowspan = 2 * tonumber(getArg(arg .. '_rows') or 1),
					id = getArg(arg .. '_id'),
				})
				:addClass(boxclass)
				:addClass(getArg(arg .. '_class'))
			local style = mergeStyles(boxstyle, arg)
			if style then
				td:attr('style', style)
			end
			td:wikitext(trim(inputArgs[arg] or ''))
			row1 = row1 .. tostring(td)
		end
	end

	-- We reached the end of the unnamed arguments. If the last argument was not
	-- a row terminator, then the buffer still contains the last row, so add that
	-- to the output.
	if row1 ~= '' then
		processedRows[#processedRows + 1] = processRow(row1, row2)
	end

	-- Insert all the table rows into an HTML `<table>`.
	local output = mw.html.create('table')
		:attr({
			class = 'diagram',
			id = getArg('id'),
			style = getArg('style') or getArg('css'),
			title = getArg('summary'),
		})
		:addClass(getArg('class'))
		:wikitext(table.concat(processedRows))  -- insert the rows
	return tostring(output)
end

return p