Module:PortalBase
From Soyjak Wiki, the free ensoyclopedia
Jump to navigationJump to search
| This module is rated as beta. It is considered ready for widespread use, but should be monitored to ensure results are as expected. |
| This module depends on the following other modules: |
This module uses TemplateStyles. Click here to edit.
This module implements {{PortalBase}}. See the template documentation for more usage info.
local getArgs = require('Module:Arguments').getArgs
local p = {}
local stylesheet = 'Module:PortalBase/styles.css'
local function cleanArgument(key, value)
if type(value) == 'string' and mw.text.trim(value) == '' then
return nil
end
return value
end
local function collectRowNumbers(args)
local numbers = {}
local seen = {}
for key in pairs(args) do
if type(key) == 'string' then
local digits = key:match('^row([1-9]%d*)$')
or key:match('^row([1-9]%d*)_')
if digits then
local number = tonumber(digits)
if not seen[number] then
seen[number] = true
numbers[#numbers + 1] = number
end
end
end
end
table.sort(numbers)
return numbers
end
local function alignmentClass(value, classPrefix)
if type(value) ~= 'string' then
return nil
end
local alignment = mw.text.trim(value):lower()
if alignment == 'left'
or alignment == 'center'
or alignment == 'right'
then
return (classPrefix or 'portal-align-') .. alignment
end
return nil
end
local function renderHeaderImage(header, content, position)
if not content then
return
end
header:tag('div')
:addClass('portal-header-image')
:addClass('portal-header-image-' .. position)
:newline()
:wikitext(content)
:newline()
end
local function renderHeader(args)
if not (
args.title
or args.subtitle
or args.header_image1
or args.header_image2
) then
return nil
end
local header = mw.html.create('div')
:addClass('portal-header')
:addClass(alignmentClass(args.header_align))
renderHeaderImage(header, args.header_image1, 'start')
if args.title or args.subtitle then
local text = header:tag('div')
:addClass('portal-header-text')
if args.title then
text:tag('div')
:addClass('portal-title')
:wikitext(args.title)
end
if args.subtitle then
text:tag('div')
:addClass('portal-subtitle')
:wikitext(args.subtitle)
end
end
renderHeaderImage(header, args.header_image2, 'end')
return header
end
local function renderSection(section)
if not section.content then
return nil
end
local card = mw.html.create('div')
:addClass('portal-card')
:addClass(section.class)
:addClass(alignmentClass(section.align))
if section.heading or section.action then
local heading = card:tag('div')
:addClass('portal-card-heading')
:addClass(alignmentClass(section.headerAlign))
if section.heading then
heading:tag('div')
:addClass('portal-card-heading-text')
:wikitext(section.heading)
end
if section.action then
heading:tag('span')
:addClass('portal-card-heading-action')
:wikitext(section.action)
end
end
card:tag('div')
:addClass('portal-card-body')
:newline()
:wikitext(section.content)
:newline()
if section.footer then
card:tag('div')
:addClass('portal-card-footer')
:newline()
:wikitext(section.footer)
:newline()
end
return card
end
local function renderFullRow(args, number)
local prefix = 'row' .. number
local section = renderSection({
heading = args[prefix .. '_header'],
action = args[prefix .. '_action'],
content = args[prefix],
footer = args[prefix .. '_footer'],
align = args[prefix .. '_align'],
headerAlign = args[prefix .. '_header_align'],
})
if not section then
return nil
end
local row = mw.html.create('div')
:addClass('portal-row')
:addClass('portal-row-full')
:addClass('portal-row-' .. number)
row:node(section)
return row
end
local function renderRawRow(args, number)
local prefix = 'row' .. number
local content = args[prefix]
if not content then
return nil
end
local row = mw.html.create('div')
:addClass('portal-row')
:addClass('portal-row-raw')
:addClass('portal-row-' .. number)
:addClass(alignmentClass(args[prefix .. '_align']))
row:newline()
:wikitext(content)
:newline()
return row
end
local function collectSectionNumbers(args, prefix)
local numbers = {}
local seen = {}
local pattern = '^' .. prefix .. '([1-9]%d*)'
for key in pairs(args) do
if type(key) == 'string' then
local digits = key:match(pattern .. '$')
or key:match(pattern .. '_')
if digits then
local number = tonumber(digits)
if not seen[number] then
seen[number] = true
numbers[#numbers + 1] = number
end
end
end
end
table.sort(numbers)
return numbers
end
local function renderColumn(args, rowPrefix, side)
local sidePrefix = rowPrefix .. '_' .. side
local cards = {}
local function addSection(prefix, number)
local class = 'portal-card-' .. side
if number then
class = class .. ' portal-card-' .. side .. '-' .. number
end
local card = renderSection({
heading = args[prefix .. '_header'],
action = args[prefix .. '_action'],
content = args[prefix],
footer = args[prefix .. '_footer'],
align = args[prefix .. '_align'] or args[sidePrefix .. '_align'],
headerAlign = args[prefix .. '_header_align'],
class = class
})
if card then
cards[#cards + 1] = tostring(card)
end
end
addSection(sidePrefix)
for _, number in ipairs(collectSectionNumbers(args, sidePrefix)) do
addSection(sidePrefix .. number, number)
end
if #cards == 0 then
return nil
end
return table.concat(cards, '\n')
end
local function normalizeFlex(value)
local number = tonumber(value)
if number and number > 0 and number < math.huge then
return tostring(number)
end
return nil
end
local function renderColumnsRow(frame, args, number)
local prefix = 'row' .. number
local left = renderColumn(args, prefix, 'left')
local right = renderColumn(args, prefix, 'right')
if not left and not right then
return nil
end
local row = mw.html.create('div')
:addClass('portal-row')
:addClass('portal-row-' .. number)
:addClass(alignmentClass(args[prefix .. '_align']))
if left and right then
row:addClass('portal-row-columns')
:newline()
:wikitext(frame:expandTemplate({
title = 'Flex columns',
args = {
[1] = left,
[2] = right,
flex1 = normalizeFlex(args[prefix .. '_flex1']),
flex2 = normalizeFlex(args[prefix .. '_flex2'])
}
}))
:newline()
else
row:addClass('portal-row-full')
:newline()
:wikitext(left or right)
:newline()
end
return row
end
local function renderTilesRow(frame, args, number)
local prefix = 'row' .. number
local itemPrefix = prefix .. '_item'
local items = {}
for _, itemNumber in ipairs(collectSectionNumbers(args, itemPrefix)) do
local key = itemPrefix .. itemNumber
local content = args[key]
if content then
local tile = mw.html.create('div')
:addClass('portal-tile')
:addClass('portal-tile-' .. itemNumber)
:addClass(alignmentClass(args[key .. '_align']))
tile:newline()
:wikitext(content)
:newline()
items[#items + 1] = tostring(tile)
end
end
if #items == 0 then
return nil
end
local group = mw.html.create('div')
:addClass('portal-tile-group')
group:newline()
:wikitext(frame:expandTemplate({
title = 'Flexbox',
args = items
}))
:newline()
local section = renderSection({
heading = args[prefix .. '_header'],
action = args[prefix .. '_action'],
content = tostring(group),
footer = args[prefix .. '_footer'],
align = args[prefix .. '_align'],
headerAlign = args[prefix .. '_header_align'],
})
local row = mw.html.create('div')
:addClass('portal-row')
:addClass('portal-row-tiles')
:addClass('portal-row-' .. number)
row:node(section)
return row
end
local function renderRow(frame, args, number)
local prefix = 'row' .. number
local layout = args[prefix .. '_layout']
if type(layout) == 'string' then
layout = mw.text.trim(layout):lower()
end
if layout == 'raw' then
return renderRawRow(args, number)
end
if layout == 'tiles' then
return renderTilesRow(frame, args, number)
end
if layout == 'columns' then
return renderColumnsRow(frame, args, number)
end
if layout == 'full' or args[prefix] then
return renderFullRow(args, number)
end
if not layout then
return renderColumnsRow(frame, args, number)
end
return renderFullRow(args, number)
end
local function renderFooter(args)
local section = renderSection({
heading = args.footer_header,
action = args.footer_action,
content = args.footer,
align = args.footer_align,
headerAlign = args.footer_header_align,
})
if not section then
return nil
end
local footer = mw.html.create('div')
:addClass('portal-footer')
footer:node(section)
return footer
end
function p.main(frame)
local args = getArgs(frame, {
valueFunc = cleanArgument
})
local portal = mw.html.create('div')
:addClass('portal-template')
:addClass(args.class)
:addClass(alignmentClass(args.align))
:addClass(alignmentClass(args.header_align, 'portal-heading-align-'))
portal:node(renderHeader(args))
local content = mw.html.create('div')
:addClass('portal-content')
local manualContent = args.content or args[1]
local hasContent = false
if manualContent then
content:addClass('portal-content-manual')
:newline()
:wikitext(manualContent)
:newline()
hasContent = true
else
local rows = collectRowNumbers(args)
for _, number in ipairs(rows) do
local row = renderRow(frame, args, number)
if row then
content:newline()
:node(row)
:newline()
hasContent = true
end
end
end
if hasContent then
portal:node(content)
end
portal:node(renderFooter(args))
local styles = frame:extensionTag({
name = 'templatestyles',
args = {
src = stylesheet
}
})
return styles .. tostring(portal)
end
return p
