Module:CRTModel: Difference between revisions
No edit summary |
No edit summary |
||
| Line 20: | Line 20: | ||
end | end | ||
local function addSectionRow( | local function addSectionRow(container, title) | ||
local | local section = container:tag('div') | ||
css( | css(section, 'grid-column: 1 / -1; text-align:center; padding:0.5em; background:#e0e0e0; font-weight:bold;') | ||
section:wikitext(title) | |||
end | end | ||
local function addRow( | local function addRow(container, label, valueHtmlOrWikitext, isRawHtml) | ||
local | local labelDiv = container:tag('div') | ||
css( | css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;') | ||
labelDiv:wikitext(label) | |||
local | local valueDiv = container:tag('div') | ||
css( | css(valueDiv, 'padding:0.2em 0.4em;') | ||
if isRawHtml then | if isRawHtml then | ||
valueDiv:node(mw.html.create('span'):wikitext(valueHtmlOrWikitext)) | |||
else | else | ||
valueDiv:wikitext(valueHtmlOrWikitext) | |||
end | end | ||
end | end | ||
local function addFullRow( | local function addFullRow(container, valueHtmlOrWikitext, cssText, isRawHtml) | ||
local | local fullDiv = container:tag('div') | ||
css(fullDiv, 'grid-column: 1 / -1; ' .. (cssText or '')) | |||
if isRawHtml then | if isRawHtml then | ||
fullDiv:wikitext(valueHtmlOrWikitext) | |||
else | else | ||
fullDiv:wikitext(valueHtmlOrWikitext) | |||
end | end | ||
end | end | ||
| Line 79: | Line 75: | ||
local standards_semantic = trim(args.standards_semantic) | local standards_semantic = trim(args.standards_semantic) | ||
local | local container = mw.html.create('div') | ||
container:addClass('infobox') | |||
container:addClass('infobox-crt-model') | |||
css( | css(container, 'float:right; clear:right; margin:0 0 1em 1em; width:22em; border:1px solid #a2a9b1; background:#f8f9fa; 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) | -- Title row (no background color, centered, bold) | ||
addFullRow( | addFullRow( | ||
container, | |||
(brand or '') .. ' ' .. (model_code or ''), | (brand or '') .. ' ' .. (model_code or ''), | ||
'text-align:center; font-size:125%; font-weight:bold; padding:0.5em;', | 'text-align:center; font-size:125%; font-weight:bold; padding:0.5em;', | ||
| Line 95: | Line 91: | ||
local file = image_main:gsub('^File:', '') | local file = image_main:gsub('^File:', '') | ||
addFullRow( | addFullRow( | ||
container, | |||
string.format('[[File:%s|frameless|300px]]', file), | string.format('[[File:%s|frameless|300px]]', file), | ||
'text-align:center; padding:0;', | 'text-align:center; padding:0;', | ||
| Line 106: | Line 102: | ||
local file = model_3d:gsub('^File:', '') | local file = model_3d:gsub('^File:', '') | ||
addFullRow( | addFullRow( | ||
container, | |||
string.format('[[File:%s]]', file), | string.format('[[File:%s]]', file), | ||
'text-align:center; padding:0;', | 'text-align:center; padding:0;', | ||
| Line 113: | Line 109: | ||
end | end | ||
addSectionRow( | addSectionRow(container, 'Overview') | ||
-- Brand row using #formredlink (must preprocess) | -- Brand row using #formredlink (must preprocess) | ||
| Line 122: | Line 118: | ||
) | ) | ||
local brandHtml = frame:preprocess(brandWikitext) | local brandHtml = frame:preprocess(brandWikitext) | ||
addRow( | addRow(container, 'Brand', brandHtml, true) | ||
end | end | ||
if model_code then | if model_code then | ||
addRow( | addRow(container, 'Model', model_code, false) | ||
else | else | ||
addRow( | addRow(container, 'Model', '', false) | ||
end | end | ||
if crt_type then | if crt_type then | ||
addRow( | addRow(container, 'Type', crt_type, false) | ||
end | end | ||
if screen_size_in then | if screen_size_in then | ||
addRow( | addRow(container, 'Screen size', screen_size_in .. '″', false) | ||
end | end | ||
if tvl then | if tvl then | ||
addRow( | addRow(container, 'TVL', tvl, false) | ||
end | end | ||
| Line 147: | Line 143: | ||
) | ) | ||
local chassisHtml = frame:preprocess(chassisWikitext) | local chassisHtml = frame:preprocess(chassisWikitext) | ||
addRow( | addRow(container, 'Chassis', chassisHtml, true) | ||
end | end | ||
-- ECID row: build as a normal external link wikitext; preprocess not required, but fine. | -- 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) | local ecidWikitext = string.format('[{{fullurl:%s|curid=%d}} EC%d]', pageName, pageId, pageId) | ||
addRow( | addRow(container, 'ECID', frame:preprocess(ecidWikitext), true) | ||
-- Dimensions section: show if we have height/width/depth OR weight | -- Dimensions section: show if we have height/width/depth OR weight | ||
if height or width or depth or weight then | if height or width or depth or weight then | ||
addSectionRow( | addSectionRow(container, 'Dimensions') | ||
if height or width or depth then | if height or width or depth then | ||
| Line 162: | Line 158: | ||
local w = width or '–' | local w = width or '–' | ||
local d = depth or '–' | local d = depth or '–' | ||
addRow( | addRow(container, 'H × W × D', string.format('%s × %s × %s', h, w, d), false) | ||
end | end | ||
| Line 168: | Line 164: | ||
if weight then | if weight then | ||
local weightWikitext = '{{#show: ' .. fullPageName .. ' |?Weight # lb }} / {{#show: ' .. fullPageName .. ' |?Weight # kg }}' | local weightWikitext = '{{#show: ' .. fullPageName .. ' |?Weight # lb }} / {{#show: ' .. fullPageName .. ' |?Weight # kg }}' | ||
addRow( | addRow(container, 'Weight', frame:preprocess(weightWikitext), true) | ||
end | end | ||
end | end | ||
| Line 188: | Line 184: | ||
-- Only display Inputs section if the query returned content (not just whitespace) | -- Only display Inputs section if the query returned content (not just whitespace) | ||
if askHtml and trim(askHtml) then | if askHtml and trim(askHtml) then | ||
addSectionRow( | addSectionRow(container, 'Inputs') | ||
addFullRow( | addFullRow(container, askHtml, 'padding:0.2em 0.4em;', true) | ||
end | end | ||
-- Only display Standards section if standards_semantic has content | -- Only display Standards section if standards_semantic has content | ||
if standards_semantic then | if standards_semantic then | ||
addSectionRow( | addSectionRow(container, 'Standards') | ||
addFullRow( | addFullRow(container, standards_semantic, 'padding:0.2em 0.4em;', false) | ||
end | end | ||
-- Documents section | -- Documents section | ||
addSectionRow( | addSectionRow(container, 'Documents') | ||
-- Build the upload link | -- Build the upload link | ||
| Line 222: | Line 218: | ||
local hasDocuments = documentCount and tonumber(trim(documentCount)) and tonumber(trim(documentCount)) > 0 | local hasDocuments = documentCount and tonumber(trim(documentCount)) and tonumber(trim(documentCount)) > 0 | ||
-- Create the content | -- Create the content div | ||
local | local docsDiv = container:tag('div') | ||
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;') | |||
css( | |||
if hasDocuments then | if hasDocuments then | ||
| Line 246: | Line 241: | ||
-- Parse the HTML table - extract data from <td> tags | -- Parse the HTML table - extract data from <td> tags | ||
if docsResult then | if docsResult then | ||
-- Match each table row | -- Match each table row | ||
| Line 274: | Line 268: | ||
end | end | ||
docsDiv:node(ul) | |||
-- Add upload link as a button | -- Add upload link as a button | ||
local uploadDiv = mw.html.create('div') | local uploadDiv = mw.html.create('div') | ||
uploadDiv:css('margin-top', '0.5em'):css('text-align', 'center') | uploadDiv:css('margin-top', '0.5em'):css('text-align', 'center') | ||
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload a document</span>]') | uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload a document</span>]') | ||
docsDiv:node(uploadDiv) | |||
else | else | ||
-- Just show the upload link as a button | -- Just show the upload link as a button | ||
local uploadDiv = mw.html.create('div') | local uploadDiv = mw.html.create('div') | ||
uploadDiv:css('text-align', 'center') | uploadDiv:css('text-align', 'center') | ||
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload a document</span>]') | uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload a document</span>]') | ||
docsDiv:node(uploadDiv) | |||
end | end | ||
| Line 341: | Line 335: | ||
-- Display links section if there are any links | -- Display links section if there are any links | ||
if hasLinks then | if hasLinks then | ||
addSectionRow( | addSectionRow(container, 'Archival Links') | ||
-- Add links in order: Database, Manufacturer, Retailer | -- Add links in order: Database, Manufacturer, Retailer | ||
| Line 352: | Line 346: | ||
table.insert(linksHtml, '[' .. link.url .. ' ' .. link.source .. ']') | table.insert(linksHtml, '[' .. link.url .. ' ' .. link.source .. ']') | ||
end | end | ||
addRow( | addRow(container, linkType, table.concat(linksHtml, '<br>'), true) | ||
end | end | ||
end | end | ||
| Line 358: | Line 352: | ||
-- Contribute section | -- Contribute section | ||
addSectionRow( | addSectionRow(container, 'Contribute') | ||
-- Build the edit link URL | -- Build the edit link URL | ||
| Line 369: | Line 363: | ||
) | ) | ||
-- Create the edit button | -- Create the edit button div | ||
local | local editDiv = container:tag('div') | ||
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;') | |||
css( | |||
-- Add edit button with primary action styling | -- Add edit button with primary action styling | ||
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#2563eb;color:#ffffff;border:1px solid #2563eb;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;cursor:pointer;">Edit this data</span>]') | |||
return tostring( | return tostring(container) | ||
end | end | ||
return p | return p | ||
Revision as of 02:59, 26 December 2025
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')
css(section, 'grid-column: 1 / -1; text-align:center; padding:0.5em; background:#e0e0e0; font-weight:bold;')
section:wikitext(title)
end
local function addRow(container, label, valueHtmlOrWikitext, isRawHtml)
local labelDiv = container:tag('div')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(label)
local valueDiv = container:tag('div')
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)
local fullDiv = container:tag('div')
css(fullDiv, 'grid-column: 1 / -1; ' .. (cssText or ''))
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 image_main = trim(args.image_main)
local model_3d = trim(args.model_3d)
local crt_type = trim(args.crt_type)
local screen_size_in = trim(args.screen_size_in)
local tvl = trim(args.tvl)
local chassis = trim(args.chassis)
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)
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; border:1px solid #a2a9b1; background:#f8f9fa; 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
)
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
)
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
)
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
if crt_type then
addRow(container, 'Type', crt_type, false)
end
if screen_size_in then
addRow(container, 'Screen size', screen_size_in .. '″', 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
-- 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
-- Inputs section: preprocess #ask so it renders, but only show section if there are results
local askWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\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)
-- Only display Inputs section if the query returned content (not just whitespace)
if askHtml and trim(askHtml) then
addSectionRow(container, 'Inputs')
addFullRow(container, askHtml, '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
-- Documents section
addSectionRow(container, 'Documents')
-- Build the upload link
local target = pageName .. " - document"
local uploadUrl = mw.uri.fullUrl(
"Special:FormEdit",
{
form = "Document",
target = target,
["Document[crt_model]"] = 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 - it returns an HTML table
local docsQueryWikitext =
'{{#ask:\n' ..
' [[Category:Documents]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |?Has document type\n' ..
' |?Has document URL\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, trim(cleanContent))
end
-- cells[1] = page name, cells[2] = type, cells[3] = URL
if #cells >= 3 then
local docType = cells[2]
local docUrl = cells[3]
if docUrl and docUrl ~= '' then
local displayText = (docType and docType ~= '') and docType or 'Document'
-- Create list item with link
local li = ul:tag('li')
li:wikitext('[' .. docUrl .. ' ' .. 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 style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload 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 style="display:inline-block;padding:0.5rem 1rem;background-color:#eff6ff;color:#1d4ed8;border:1px solid #bfdbfe;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;">Upload 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
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span style="display:inline-block;padding:0.5rem 1rem;background-color:#2563eb;color:#ffffff;border:1px solid #2563eb;border-radius:0.375rem;font-size:0.875rem;font-weight:500;text-decoration:none;cursor:pointer;">Edit this data</span>]')
return tostring(container)
end
return p