Module:Tree chart
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Tree chart/doc
-- Module:Tree chart
-- Defensive renderer that emits a scoped inline stylesheet next to the table.
pcall(function() require('strict') end)
local p = {}
local cells = mw.loadData('Module:Tree chart/data')
-- safe wrapper to insert raw HTML fragments into mw.html nodes
local function safe_wikitext(node, html)
node:wikitext(html)
end
function p._main(cell_args)
local out = mw.html.create()
-- Inline style block scoped to .tree-chart-inline-style (highly specific, uses !important)
local style = [[
<style type="text/css">
/* Scoped tree styles that override global rules */
.tree-chart-inline-style { border-collapse: collapse !important; border-spacing: 0 !important; vertical-align: top !important; display: inline-table !important; font-family: inherit !important; }
.tree-chart-inline-style td { padding: 0 !important; margin: 0 !important; vertical-align: top !important; box-sizing: border-box !important; white-space: nowrap !important; line-height: 1 !important; font-size: 0.9rem !important; }
.tree-chart-inline-style td.connector { padding: 0 !important; width:1em !important; height:1em !important; min-width:1em !important; min-height:1em !important; max-width:1em !important; max-height:1em !important; line-height:1 !important; }
.tree-chart-inline-style td.box { padding: 0.35em 0.9em !important; white-space: normal !important; font-size: 1rem !important; text-align: center !important; }
.tree-chart-inline-style td[style*="border-bottom"], .tree-chart-inline-style td[style*="border-right"], .tree-chart-inline-style td[style*="border"] { box-sizing: border-box !important; }
</style>
]]
out:wikitext(style)
-- Build table with explicit style attribute (avoid :css camelCase pitfalls)
local tbl = out:tag('table')
:addClass('tree-chart-inline-style')
:attr{ style = 'border-collapse: collapse; border-spacing: 0; vertical-align: top;' }
local top = tbl:tag('tr'):attr{ style = 'height:1px; text-align:center;' }
local bottom = tbl:tag('tr'):attr{ style = 'height:1px; text-align:center;' }
for _, v in ipairs(cell_args) do
if type(v) == 'string' then
local celldef = cells[v]
if celldef then
-- celldef.t / celldef.b are fragments produced by Module:Tree chart/data (strings)
-- Tag their <td> elements as connector cells by adding class="connector"
if celldef.t then
local tstr = tostring(celldef.t)
-- add class only if not already present
tstr = tstr:gsub('<td(.-)>', function(attrs)
-- if class attribute exists, append connector; otherwise add it
if attrs:match('class%s*=') then
return '<td' .. attrs:gsub('class%s*=%s*"(.-)"', function(c) return string.format(' class="%s connector"', c) end) .. '>'
else
return '<td' .. attrs .. ' class="connector">'
end
end)
safe_wikitext(top, tstr)
else
top:tag('td'):addClass('connector'):attr{ style = 'width:1em; height:1em;' }
end
if celldef.b then
local bstr = tostring(celldef.b)
bstr = bstr:gsub('<td(.-)>', function(attrs)
if attrs:match('class%s*=') then
return '<td' .. attrs:gsub('class%s*=%s*"(.-)"', function(c) return string.format(' class="%s connector"', c) end) .. '>'
else
return '<td' .. attrs .. ' class="connector">'
end
end)
safe_wikitext(bottom, bstr)
else
bottom:tag('td'):addClass('connector'):attr{ style = 'width:1em; height:1em;' }
end
else
-- unknown symbol: render empty connector td
top:tag('td'):addClass('connector'):attr{ style = 'width:1em; height:1em;' }
bottom:tag('td'):addClass('connector'):attr{ style = 'width:1em; height:1em;' }
end
else
-- custom cell with text (a boxed cell)
local colspan = v.colspan or cell_args.colspan or 6
local rowspan = v.rowspan or cell_args.rowspan or 2
local border_prop = v.border or cell_args.border or '2'
local border = tostring(border_prop) .. 'px solid'
top:tag('td')
:addClass('box')
:attr{
colspan = tostring(colspan),
rowspan = tostring(rowspan),
style = 'padding:0.35em 0.9em; border: ' .. border .. '; box-sizing: border-box;'
}
:wikitext(v.text or '')
-- if rowspan < 2 we need a bottom placeholder; most boxes use rowspan=2 so bottom is empty
if not tonumber(rowspan) or tonumber(rowspan) < 2 then
bottom:tag('td'):addClass('connector'):attr{ style = 'width:1em; height:1em;' }
end
end
end
return tostring(tbl)
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame,
{ wrappers = 'Template:Tree chart', trim = false, removeBlanks = false })
local cell_args = {
colspan = args.colspan,
rowspan = args.rowspan,
border = args.border,
boxstyle = args.boxstyle
}
for _, val in ipairs(args) do
local trimmedVal = val:match('^%s*(.-)%s*$')
if trimmedVal == '' then trimmedVal = '$' end
if cells[trimmedVal] then
table.insert(cell_args, trimmedVal)
else
local rightTrimmedVal = val:gsub('%s+$','')
local custom = {
text = args[trimmedVal] or ('{{{'..trimmedVal..'}}}'),
colspan = args['colspan_'..rightTrimmedVal],
rowspan = args['rowspan_'..rightTrimmedVal],
border = args['border_'..rightTrimmedVal],
boxstyle = args['boxstyle_'..rightTrimmedVal]
}
table.insert(cell_args, custom)
end
end
return p._main(cell_args)
end
return p