Module:CRTModel: Difference between revisions
Add speaker display section to infobox (via update-page on MediaWiki MCP Server) |
Update Power section to query voltage ranges from subobjects instead of template parameters (via update-page on MediaWiki MCP Server) |
||
| Line 87: | Line 87: | ||
local standards_semantic = trim(args.standards_semantic) | local standards_semantic = trim(args.standards_semantic) | ||
-- Power specifications | -- Power specifications (frequency and power consumption still from template args) | ||
local frequency = trim(args.frequency) | local frequency = trim(args.frequency) | ||
local power_avg = trim(args.power_avg) | local power_avg = trim(args.power_avg) | ||
| Line 262: | Line 260: | ||
end | end | ||
-- Power section: | -- Power section: query voltage ranges from subobjects | ||
if | -- First, query for voltage range subobjects | ||
local voltageQueryWikitext = | |||
'{{#ask:\n' .. | |||
' [[Has CRT model::' .. fullPageName .. ']]\n' .. | |||
' [[Voltage min::+]]\n' .. | |||
' |?Voltage min\n' .. | |||
' |?Voltage max\n' .. | |||
' |format=broadtable\n' .. | |||
' |headers=hide\n' .. | |||
' |link=none\n' .. | |||
' |sort=Voltage min\n' .. | |||
' |order=asc\n' .. | |||
'}}' | |||
local voltageResult = frame:preprocess(voltageQueryWikitext) | |||
-- Parse voltage ranges from broadtable result | |||
local voltageRanges = {} | |||
if voltageResult then | |||
for rowContent in voltageResult:gmatch('<tr[^>]*>(.-)</tr>') do | |||
local cells = {} | |||
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | |||
local cleanContent = cellContent:gsub('<[^>]+>', '') | |||
table.insert(cells, trim(cleanContent)) | |||
end | |||
-- cells[1] = subobject, cells[2] = voltage_min, cells[3] = voltage_max | |||
if #cells >= 3 then | |||
local vMin = tonumber(cells[2]) | |||
local vMax = tonumber(cells[3]) | |||
if vMin or vMax then | |||
table.insert(voltageRanges, {min = vMin, max = vMax}) | |||
end | |||
end | |||
end | |||
end | |||
local hasVoltageRanges = #voltageRanges > 0 | |||
-- Show Power section if we have voltage ranges, frequency, or power consumption | |||
if hasVoltageRanges or frequency or power_avg or power_max then | |||
addSectionRow(container, 'Power') | addSectionRow(container, 'Power') | ||
-- Voltage row: display | -- Voltage row: display ranges from subobjects | ||
if | if hasVoltageRanges then | ||
local | local rangeStrings = {} | ||
if | for _, range in ipairs(voltageRanges) do | ||
local rangeStr | |||
if range.min and range.max then | |||
if range.min == range.max then | |||
-- Single voltage (e.g., "120V") | |||
rangeStr = range.min .. 'V' | |||
else | |||
-- Range (e.g., "100–120V") | |||
rangeStr = range.min .. '–' .. range.max .. 'V' | |||
end | |||
elseif range.min then | |||
rangeStr = range.min .. 'V' | |||
elseif range.max then | |||
rangeStr = range.max .. 'V' | |||
end | end | ||
if rangeStr then | |||
table.insert(rangeStrings, rangeStr) | |||
end | |||
end | |||
if #rangeStrings > 0 then | |||
-- Join multiple ranges with " / " (e.g., "100–120V / 220–240V") | |||
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false) | |||
end | end | ||
end | end | ||
Revision as of 20:00, 15 January 2026
Documentation for this module may be created at Module:CRTModel/doc
-- Module:CRTModel
-- Builds the CRT infobox as strict HTML to avoid whitespace -> <p><br></p>
-- Uses frame:preprocess() so parser functions like #ask and #formredlink evaluate.
local p = {}
local function trim(s)
if s == nil then return nil end
s = tostring(s)
s = s:gsub('^%s+', ''):gsub('%s+$', '')
if s == '' then return nil end
return s
end
local function css(node, cssText)
if cssText and cssText ~= '' then
node:cssText(cssText)
end
return node
end
local function addSectionRow(container, title)
local section = container:tag('div')
section:addClass('infobox-section-header')
css(section, 'grid-column: 1 / -1; text-align:center; padding:0.5em; font-weight:bold;')
section:wikitext(title)
end
local function addRow(container, label, valueHtmlOrWikitext, isRawHtml)
local labelDiv = container:tag('div')
labelDiv:addClass('infobox-label')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(label)
local valueDiv = container:tag('div')
valueDiv:addClass('infobox-value')
css(valueDiv, 'padding:0.2em 0.4em;')
if isRawHtml then
valueDiv:node(mw.html.create('span'):wikitext(valueHtmlOrWikitext))
else
valueDiv:wikitext(valueHtmlOrWikitext)
end
end
local function addFullRow(container, valueHtmlOrWikitext, cssText, isRawHtml, extraClass)
local fullDiv = container:tag('div')
css(fullDiv, 'grid-column: 1 / -1; ' .. (cssText or ''))
if extraClass then
fullDiv:addClass(extraClass)
end
if isRawHtml then
fullDiv:wikitext(valueHtmlOrWikitext)
else
fullDiv:wikitext(valueHtmlOrWikitext)
end
end
function p.infobox(frame)
local parent = frame:getParent()
local args = parent and parent.args or frame.args
local title = mw.title.getCurrentTitle()
local pageName = title.text
local fullPageName = title.fullText
local pageId = title.id or 0
local brand = trim(args.brand)
local model_code = trim(args.model_code)
local variants = trim(args.variants)
local series = trim(args.series)
local image_main = trim(args.image_main)
local model_3d = trim(args.model_3d)
local crt_type = trim(args.crt_type)
local form_factor = trim(args.form_factor)
local built_in_devices = trim(args.built_in_devices)
local screen_size_in = trim(args.screen_size_in)
local aspect_ratio = trim(args.aspect_ratio)
local tvl = trim(args.tvl)
local chassis = trim(args.chassis)
local remote_control = trim(args.remote_control)
local height = trim(args.height)
local width = trim(args.width)
local depth = trim(args.depth)
local weight = trim(args.weight)
local standards_semantic = trim(args.standards_semantic)
-- Power specifications (frequency and power consumption still from template args)
local frequency = trim(args.frequency)
local power_avg = trim(args.power_avg)
local power_max = trim(args.power_max)
-- TV system support
local av_system = trim(args.av_system)
local tuner_system = trim(args.tuner_system)
-- Scan range
local h_scan_min = trim(args.h_scan_min)
local h_scan_max = trim(args.h_scan_max)
local v_scan_min = trim(args.v_scan_min)
local v_scan_max = trim(args.v_scan_max)
-- Speakers
local speaker_type = trim(args.speaker_type)
local speaker_watts = trim(args.speaker_watts)
local subwoofer = trim(args.subwoofer)
-- Use frame:extensionTag to create a proper <style> tag that won't be escaped
local styleTag = frame:extensionTag('templatestyles', '', {src = 'Template:CRT model/styles.css'})
local container = mw.html.create('div')
container:addClass('infobox')
container:addClass('infobox-crt-model')
css(container, 'float:right; clear:right; margin:0 0 1em 1em; width:22em; font-size:88%; line-height:1.5em; display:grid; grid-template-columns:auto 1fr; gap:3px; padding:3px;')
-- Title row (no background color, centered, bold)
addFullRow(
container,
(brand or '') .. ' ' .. (model_code or ''),
'text-align:center; font-size:125%; font-weight:bold; padding:0.5em;',
false,
'infobox-title'
)
if image_main then
local file = image_main:gsub('^File:', '')
addFullRow(
container,
string.format('[[File:%s|frameless|300px]]', file),
'text-align:center; padding:0;',
false,
'infobox-image'
)
end
-- 3D model display (right below main photo)
if model_3d then
local file = model_3d:gsub('^File:', '')
addFullRow(
container,
string.format('[[File:%s]]', file),
'text-align:center; padding:0;',
false,
'infobox-3d-model'
)
end
addSectionRow(container, 'Overview')
-- Brand row using #formredlink (must preprocess)
if brand then
local brandWikitext = string.format(
'{{#formredlink: target=%s |form=Manufacturer |existing page link text=%s }}',
brand, brand
)
local brandHtml = frame:preprocess(brandWikitext)
addRow(container, 'Brand', brandHtml, true)
end
if model_code then
addRow(container, 'Model', model_code, false)
else
addRow(container, 'Model', '', false)
end
-- Regional variants row
if variants then
addRow(container, 'Variants', variants, false)
end
if series then
local seriesWikitext = string.format(
'{{#formredlink: target=%s |form=Series |existing page link text=%s }}',
series, series
)
local seriesHtml = frame:preprocess(seriesWikitext)
addRow(container, 'Series', seriesHtml, true)
end
if crt_type then
addRow(container, 'Type', crt_type, false)
end
-- Form factor row (only show if not "Standard")
if form_factor and form_factor ~= 'Standard' then
addRow(container, 'Form factor', form_factor, false)
end
-- Built-in devices row
if built_in_devices then
addRow(container, 'Built-in', built_in_devices, false)
end
if screen_size_in then
addRow(container, 'Screen size', screen_size_in .. '″', false)
end
if aspect_ratio then
addRow(container, 'Aspect ratio', aspect_ratio, false)
end
if tvl then
addRow(container, 'TVL', tvl, false)
end
if chassis then
local chassisWikitext = string.format(
'{{#formredlink: target=%s |form=Chassis |existing page link text=%s }}',
chassis, chassis
)
local chassisHtml = frame:preprocess(chassisWikitext)
addRow(container, 'Chassis', chassisHtml, true)
end
if remote_control then
local remoteWikitext = string.format(
'{{#formredlink: target=%s |form=Remote control |existing page link text=%s }}',
remote_control, remote_control
)
local remoteHtml = frame:preprocess(remoteWikitext)
addRow(container, 'Remote', remoteHtml, true)
end
-- ECID row: build as a normal external link wikitext; preprocess not required, but fine.
local ecidWikitext = string.format('[{{fullurl:%s|curid=%d}} EC%d]', pageName, pageId, pageId)
addRow(container, 'ECID', frame:preprocess(ecidWikitext), true)
-- Dimensions section: show if we have height/width/depth OR weight
if height or width or depth or weight then
addSectionRow(container, 'Dimensions')
if height or width or depth then
local h = height or '–'
local w = width or '–'
local d = depth or '–'
addRow(container, 'H × W × D', string.format('%s × %s × %s', h, w, d), false)
end
-- Weight row: uses #show, so preprocess it
if weight then
local weightWikitext = '{{#show: ' .. fullPageName .. ' |?Weight # lb }} / {{#show: ' .. fullPageName .. ' |?Weight # kg }}'
addRow(container, 'Weight', frame:preprocess(weightWikitext), true)
end
end
-- Speakers section: show if we have any speaker specs
if speaker_type or speaker_watts or subwoofer then
addSectionRow(container, 'Speakers')
if speaker_type then
addRow(container, 'Type', speaker_type, false)
end
if speaker_watts then
addRow(container, 'Power', speaker_watts, false)
end
if subwoofer then
addRow(container, 'Subwoofer', subwoofer, false)
end
end
-- Power section: query voltage ranges from subobjects
-- First, query for voltage range subobjects
local voltageQueryWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Voltage min::+]]\n' ..
' |?Voltage min\n' ..
' |?Voltage max\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
' |sort=Voltage min\n' ..
' |order=asc\n' ..
'}}'
local voltageResult = frame:preprocess(voltageQueryWikitext)
-- Parse voltage ranges from broadtable result
local voltageRanges = {}
if voltageResult then
for rowContent in voltageResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
-- cells[1] = subobject, cells[2] = voltage_min, cells[3] = voltage_max
if #cells >= 3 then
local vMin = tonumber(cells[2])
local vMax = tonumber(cells[3])
if vMin or vMax then
table.insert(voltageRanges, {min = vMin, max = vMax})
end
end
end
end
local hasVoltageRanges = #voltageRanges > 0
-- Show Power section if we have voltage ranges, frequency, or power consumption
if hasVoltageRanges or frequency or power_avg or power_max then
addSectionRow(container, 'Power')
-- Voltage row: display ranges from subobjects
if hasVoltageRanges then
local rangeStrings = {}
for _, range in ipairs(voltageRanges) do
local rangeStr
if range.min and range.max then
if range.min == range.max then
-- Single voltage (e.g., "120V")
rangeStr = range.min .. 'V'
else
-- Range (e.g., "100–120V")
rangeStr = range.min .. '–' .. range.max .. 'V'
end
elseif range.min then
rangeStr = range.min .. 'V'
elseif range.max then
rangeStr = range.max .. 'V'
end
if rangeStr then
table.insert(rangeStrings, rangeStr)
end
end
if #rangeStrings > 0 then
-- Join multiple ranges with " / " (e.g., "100–120V / 220–240V")
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false)
end
end
-- Frequency row
if frequency then
addRow(container, 'Frequency', frequency, false)
end
-- Power consumption row
if power_avg or power_max then
local powerDisplay
if power_avg and power_max then
powerDisplay = power_avg .. 'W / ' .. power_max .. 'W<br><small>(avg / max)</small>'
elseif power_avg then
powerDisplay = power_avg .. 'W <small>(avg)</small>'
else
powerDisplay = power_max .. 'W <small>(max)</small>'
end
addRow(container, 'Consumption', powerDisplay, true)
end
end
-- TV Systems section: show if we have AV system or tuner system
if av_system or tuner_system then
addSectionRow(container, 'TV Systems')
-- AV system row (baseband video color support)
if av_system then
-- Link each system to its wiki page
local systems = {}
for system in av_system:gmatch('[^,]+') do
local s = trim(system)
if s then
table.insert(systems, '[[' .. s .. '|' .. s .. ']]')
end
end
if #systems > 0 then
addRow(container, 'AV system', table.concat(systems, ', '), true)
end
end
-- Tuner system row (RF broadcast standards)
if tuner_system then
-- Link each system to its wiki page
local systems = {}
for system in tuner_system:gmatch('[^,]+') do
local s = trim(system)
if s then
table.insert(systems, '[[' .. s .. '|' .. s .. ']]')
end
end
if #systems > 0 then
addRow(container, 'Tuner', table.concat(systems, ', '), true)
end
end
end
-- Scan Range section: show if we have any scan range specs
if h_scan_min or h_scan_max or v_scan_min or v_scan_max then
addSectionRow(container, 'Scan Range')
-- Horizontal scan row
if h_scan_min or h_scan_max then
local hDisplay
if h_scan_min and h_scan_max then
if h_scan_min == h_scan_max then
-- Single frequency (e.g., "15.7 kHz")
hDisplay = h_scan_min .. ' kHz'
else
-- Range (e.g., "15–100 kHz")
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz'
end
elseif h_scan_min then
hDisplay = h_scan_min .. ' kHz'
else
hDisplay = h_scan_max .. ' kHz'
end
addRow(container, 'Horizontal', hDisplay, false)
end
-- Vertical scan row
if v_scan_min or v_scan_max then
local vDisplay
if v_scan_min and v_scan_max then
if v_scan_min == v_scan_max then
-- Single frequency (e.g., "60 Hz")
vDisplay = v_scan_min .. ' Hz'
else
-- Range (e.g., "50–160 Hz")
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz'
end
elseif v_scan_min then
vDisplay = v_scan_min .. ' Hz'
else
vDisplay = v_scan_max .. ' Hz'
end
addRow(container, 'Vertical', vDisplay, false)
end
end
-- Inputs section: use count query to check if there are any inputs
local inputsCountWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has video signal format::+]]\n' ..
' |format=count\n' ..
'}}'
local inputCount = frame:preprocess(inputsCountWikitext)
local hasInputs = inputCount and tonumber(trim(inputCount)) and tonumber(trim(inputCount)) > 0
if hasInputs then
local askWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has video signal format::+]]\n' ..
' |?Has video signal format=signal\n' ..
' |?Has connector type=connector\n' ..
' |?Has input count=count\n' ..
' |?Has input location=location\n' ..
' |format=ul\n' ..
' |template=CRT input inline\n' ..
' |named args=yes\n' ..
' |searchlabel=\n' ..
'}}'
local askHtml = frame:preprocess(askWikitext)
addSectionRow(container, 'Inputs')
addFullRow(container, askHtml, 'padding:0.2em 0.4em;', true)
end
-- Outputs section: use count query to check if there are any outputs
local outputsCountWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has output signal format::+]]\n' ..
' |format=count\n' ..
'}}'
local outputCount = frame:preprocess(outputsCountWikitext)
local hasOutputs = outputCount and tonumber(trim(outputCount)) and tonumber(trim(outputCount)) > 0
if hasOutputs then
local outputsAskWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has output signal format::+]]\n' ..
' |?Has output type=type\n' ..
' |?Has output signal format=signal\n' ..
' |?Has output connector=connector\n' ..
' |?Has output count=count\n' ..
' |?Has output label=label\n' ..
' |?Has output location=location\n' ..
' |format=ul\n' ..
' |template=CRT output inline\n' ..
' |named args=yes\n' ..
' |searchlabel=\n' ..
'}}'
local outputsHtml = frame:preprocess(outputsAskWikitext)
addSectionRow(container, 'Outputs')
addFullRow(container, outputsHtml, 'padding:0.2em 0.4em;', true)
end
-- Only display Standards section if standards_semantic has content
if standards_semantic then
addSectionRow(container, 'Standards')
addFullRow(container, standards_semantic, 'padding:0.2em 0.4em;', false)
end
-- Mods section: Query for mod subobjects and display
local modsQueryWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has mod URL::+]]\n' ..
' |?Has mod type=type\n' ..
' |?Has mod description=description\n' ..
' |?Has mod feasible=feasible\n' ..
' |?Has mod URL=url\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
local modsResult = frame:preprocess(modsQueryWikitext)
-- Parse mods from broadtable result
local mods = {}
if modsResult then
for rowContent in modsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
if #cells >= 3 then
local modData = {
type = nil,
description = nil,
feasible = nil,
url = nil
}
for i = 2, #cells do
local cell = cells[i]
if cell then
if cell:match('^https?://') then
modData.url = cell
elseif cell == 'Yes' or cell == 'No' then
modData.feasible = cell
elseif cell == 'RGB' or cell == 'S-video' or cell == 'Other' then
modData.type = cell
else
modData.description = cell
end
end
end
if modData.url and modData.type then
table.insert(mods, modData)
end
end
end
end
-- Display mods section if there are any
if #mods > 0 then
addSectionRow(container, 'Mods')
local modsDiv = container:tag('div')
css(modsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
local ul = modsDiv:tag('ul')
ul:css('margin', '0'):css('padding-left', '1.5em')
for _, mod in ipairs(mods) do
local li = ul:tag('li')
-- Determine display text: use description if available, otherwise type
local displayText = mod.description or mod.type
-- Check if mod is not feasible
local isFeasible = mod.feasible ~= 'No'
if isFeasible then
-- Normal link for feasible mods using wikitext external link
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']')
else
-- Red styling with X for impossible mods
local wrapper = li:tag('span')
wrapper:addClass('mod-not-feasible')
wrapper:wikitext('✗ [' .. mod.url .. ' ' .. displayText .. '] ')
local notPossible = wrapper:tag('i')
notPossible:css('font-size', '0.9em')
notPossible:wikitext('(not possible)')
end
end
end
-- Documents section
addSectionRow(container, 'Documents')
-- Build the upload link with pseudorandom unique identifier
local timestamp = os.time()
math.randomseed(timestamp + pageId) -- Seed with timestamp + page ID for better randomness
local random = math.random(1000, 9999)
local target = "Document:" .. timestamp .. "-" .. random
local uploadUrl = mw.uri.fullUrl(
"Special:FormEdit",
{
form = "Document",
target = target,
["Document[crt_models]"] = pageName
}
)
-- Query for document count
local documentsCountWikitext =
'{{#ask:\n' ..
' [[Category:Documents]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |format=count\n' ..
'}}'
local documentCount = frame:preprocess(documentsCountWikitext)
local hasDocuments = documentCount and tonumber(trim(documentCount)) and tonumber(trim(documentCount)) > 0
-- Create the content div
local docsDiv = container:tag('div')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
if hasDocuments then
-- Use #ask to get documents
local docsQueryWikitext =
'{{#ask:\n' ..
' [[Category:Documents]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |?Has document type\n' ..
' |?Has document URL\n' ..
' |?Has document file\n' ..
' |?Has document title\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
local docsResult = frame:preprocess(docsQueryWikitext)
-- Create UL element
local ul = mw.html.create('ul')
ul:css('margin', '0'):css('padding-left', '1.5em')
-- Parse the HTML table - extract data from <td> tags
if docsResult then
-- Match each table row
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, cleanContent)
end
-- cells[1] = page name, cells[2] = type, cells[3] = URL, cells[4] = file, cells[5] = title
if #cells >= 2 then
local docType = trim(cells[2])
local docUrl = trim(cells[3])
local docFile = trim(cells[4])
local docTitle = trim(cells[5])
-- Determine what to link to (URL or file)
local linkTarget = nil
if docUrl then
linkTarget = docUrl
elseif docFile then
-- Extract filename from "File:..." format if present
local filename = docFile:match('File:(.+)') or docFile
linkTarget = tostring(mw.uri.fullUrl('File:' .. filename))
end
-- Always link to the wiki page
local docPageName = trim(cells[1])
if docPageName then
-- Determine display text priority: custom title > type > default
local displayText = 'Document'
if docTitle then
displayText = docTitle
elseif docType then
displayText = docType
end
-- Create list item with link to wiki page
local li = ul:tag('li')
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]')
end
end
end
end
docsDiv:node(ul)
-- Add upload link as a button
local uploadDiv = mw.html.create('div')
uploadDiv:css('margin-top', '0.5em'):css('text-align', 'center')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add a document</span>]')
docsDiv:node(uploadDiv)
else
-- Just show the upload link as a button
local uploadDiv = mw.html.create('div')
uploadDiv:css('text-align', 'center')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add a document</span>]')
docsDiv:node(uploadDiv)
end
-- Links section: Query for link subobjects and display by type
local linksQueryWikitext =
'{{#ask:\n' ..
' [[CRT model::' .. fullPageName .. ']]\n' ..
' |?Model link type\n' ..
' |?Model link source\n' ..
' |?Model link URL\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
local linksResult = frame:preprocess(linksQueryWikitext)
-- Parse links and organize by type
local linksByType = {
Manufacturer = {},
Retailer = {},
Database = {}
}
local hasLinks = false
if linksResult then
-- Match each table row
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
-- cells[1] = page/subobject, cells[2] = type, cells[3] = source, cells[4] = URL
if #cells >= 4 then
local linkType = cells[2]
local linkSource = cells[3]
local linkUrl = cells[4]
if linkType and linkSource and linkUrl and linkUrl ~= '' then
if linksByType[linkType] then
table.insert(linksByType[linkType], {source = linkSource, url = linkUrl})
hasLinks = true
end
end
end
end
end
-- Display links section if there are any links
if hasLinks then
addSectionRow(container, 'Archival Links')
-- Add links in order: Database, Manufacturer, Retailer
local typeOrder = {'Database', 'Manufacturer', 'Retailer'}
for _, linkType in ipairs(typeOrder) do
if #linksByType[linkType] > 0 then
-- If multiple links of the same type, combine them in one row
local linksHtml = {}
for _, link in ipairs(linksByType[linkType]) do
table.insert(linksHtml, '[' .. link.url .. ' ' .. link.source .. ']')
end
addRow(container, linkType, table.concat(linksHtml, '<br>'), true)
end
end
end
-- Contribute section
addSectionRow(container, 'Contribute')
-- Build the edit link URL
local editUrl = mw.uri.fullUrl(
"Special:FormStart",
{
form = "CRT model",
page_name = pageName
}
)
-- Create the edit button div
local editDiv = container:tag('div')
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
-- Add edit button with primary action styling (using class for night mode compatibility)
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span class="infobox-button infobox-button-primary">Edit this data</span>]')
-- Return templatestyles tag + container HTML
return styleTag .. tostring(container)
end
return p