Documentation for this module may be created at Module:Accessory/doc
-- Module:Accessory
-- Builds the Accessory infobox (remotes, stands, option cards, etc.)
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
-- Core fields
local brand = trim(args.brand)
local model_code = trim(args.model_code)
local accessory_type = trim(args.accessory_type)
local description = trim(args.description)
local image_main = trim(args.image_main)
-- Option card specific
local card_signals = trim(args.card_signals)
-- Media specific
local media_format = trim(args.media_format)
-- Load stylesheet
local styleTag = frame:extensionTag('templatestyles', '', {src = 'Template:Infobox styles.css'})
local container = mw.html.create('div')
container:addClass('infobox')
container:addClass('infobox-accessory')
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
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
addSectionRow(container, 'Overview')
-- Brand row using #formredlink
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)
end
if accessory_type then
addRow(container, 'Type', accessory_type, false)
end
if description then
addRow(container, 'Description', description, false)
end
-- Type-specific fields
if accessory_type == 'Option card' and card_signals then
addRow(container, 'Signals', card_signals, false)
end
if accessory_type == 'Media' and media_format then
addRow(container, 'Format', media_format, false)
end
-- ECID row
local ecidWikitext = string.format('[{{fullurl:%s|curid=%d}} EC%d]', pageName, pageId, pageId)
addRow(container, 'ECID', frame:preprocess(ecidWikitext), true)
-- Compatible CRT models section
local crtCountWikitext =
'{{#ask:\n' ..
' [[Category:CRT models]]\n' ..
' [[Has accessory::' .. fullPageName .. ']]\n' ..
' |format=count\n' ..
'}}'
local crtCountFromForward = frame:preprocess(crtCountWikitext)
-- Also check reverse links (Has related CRT model set on this accessory page)
local crtReverseCountWikitext =
'{{#ask:\n' ..
' [[Category:CRT models]]\n' ..
' [[Has related accessory::' .. fullPageName .. ']]\n' ..
' |format=count\n' ..
'}}'
local crtCountFromReverse = frame:preprocess(crtReverseCountWikitext)
-- Query for CRTs that have this accessory via forward link (remote_control field)
-- OR via Has related CRT model property set on this accessory
local hasCRTs = (tonumber(trim(crtCountFromForward)) or 0) > 0
-- Also query CRTs listed in the accessory's crt_models field
local crt_models = trim(args.crt_models)
local hasCRTsFromField = crt_models and crt_models ~= ''
if hasCRTs or hasCRTsFromField then
addSectionRow(container, 'Compatible CRT Models')
local crtDiv = container:tag('div')
css(crtDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
-- Query CRTs that reference this accessory via Has accessory property
-- (This handles forward references like remote_control field)
local crtQueryWikitext =
'{{#ask:\n' ..
' [[Category:CRT models]]\n' ..
' [[Has accessory::' .. fullPageName .. ']]\n' ..
' |?Has image#70px;x70px=Photo\n' ..
' |?Has model code=Model\n' ..
' |?Has screen size inches=Size\n' ..
' |format=table\n' ..
' |class=wikitable\n' ..
' |mainlabel=CRT model\n' ..
' |link=subject\n' ..
'}}'
-- If we have CRTs from the crt_models field, also query those
if hasCRTsFromField then
-- Parse crt_models and create OR conditions
local crtConditions = {}
for crt in crt_models:gmatch('[^,]+') do
local c = trim(crt)
if c then
table.insert(crtConditions, '[[' .. c .. ']]')
end
end
if #crtConditions > 0 then
crtQueryWikitext =
'{{#ask:\n' ..
' [[Category:CRT models]]\n' ..
' <q>' .. table.concat(crtConditions, ' OR ') .. '</q>\n' ..
' |?Has image#70px;x70px=Photo\n' ..
' |?Has model code=Model\n' ..
' |?Has screen size inches=Size\n' ..
' |format=table\n' ..
' |class=wikitable\n' ..
' |mainlabel=CRT model\n' ..
' |link=subject\n' ..
'}}'
end
end
local crtHtml = frame:preprocess(crtQueryWikitext)
crtDiv:wikitext(crtHtml)
end
-- Compatible AV devices section
local av_devices = trim(args.av_devices)
if av_devices and av_devices ~= '' then
addSectionRow(container, 'Compatible AV Devices')
local avDiv = container:tag('div')
css(avDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
-- Parse av_devices and create OR conditions
local avConditions = {}
for av in av_devices:gmatch('[^,]+') do
local a = trim(av)
if a then
table.insert(avConditions, '[[' .. a .. ']]')
end
end
if #avConditions > 0 then
local avQueryWikitext =
'{{#ask:\n' ..
' [[Category:AV devices]]\n' ..
' <q>' .. table.concat(avConditions, ' OR ') .. '</q>\n' ..
' |?Has image#70px;x70px=Photo\n' ..
' |?Has model code=Model\n' ..
' |?Has device type=Type\n' ..
' |format=table\n' ..
' |class=wikitable\n' ..
' |mainlabel=AV device\n' ..
' |link=subject\n' ..
'}}'
local avHtml = frame:preprocess(avQueryWikitext)
avDiv:wikitext(avHtml)
end
end
-- Documents section
addSectionRow(container, 'Documents')
-- Build upload link
local timestamp = os.time()
math.randomseed(timestamp + pageId)
local random = math.random(1000, 9999)
local target = "Document:" .. timestamp .. "-" .. random
local uploadUrl = mw.uri.fullUrl(
"Special:FormEdit",
{
form = "Document",
target = target
}
)
local docsDiv = container:tag('div')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
docsDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add a document</span>]')
-- Archival Links section: Query for link subobjects and display by type
local linksQueryWikitext =
'{{#ask:\n' ..
' [[Accessory::' .. 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
for rowContent in linksResult: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] = 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
if hasLinks then
addSectionRow(container, 'Archival Links')
local typeOrder = {'Database', 'Manufacturer', 'Retailer'}
for _, linkType in ipairs(typeOrder) do
if #linksByType[linkType] > 0 then
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')
local editUrl = mw.uri.fullUrl(
"Special:FormStart",
{
form = "Accessory",
page_name = pageName
}
)
local editDiv = container:tag('div')
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span class="infobox-button infobox-button-primary">Edit this data</span>]')
return styleTag .. tostring(container)
end
return p