Module:CRTModel: Difference between revisions

Add Accessories section, smart remote display (strips redundant brand prefix) (via update-page on MediaWiki MCP Server)
Change "Add accessory" button to link to Create_an_accessory with crt_models parameter (via update-page on MediaWiki MCP Server)
 
(24 intermediate revisions by the same user not shown)
Line 60: Line 60:
-- Helper function to extract model code from "Brand Model" format
-- Helper function to extract model code from "Brand Model" format
-- If the accessory starts with the same brand as the CRT, strip it for cleaner display
-- If the accessory starts with the same brand as the CRT, strip it for cleaner display
local function getAccessoryDisplayText(accessoryName, crtBrand)
local function getDisplayTextWithoutBrand(itemName, deviceBrand)
if not accessoryName then return nil end
if not itemName then return nil end
if not crtBrand then return accessoryName end
if not deviceBrand then return itemName end
-- Check if accessory name starts with the CRT brand followed by a space
-- Check if item name starts with the device brand followed by a space
local brandPrefix = crtBrand .. ' '
local brandPrefix = deviceBrand .. ' '
if accessoryName:sub(1, #brandPrefix) == brandPrefix then
if itemName:sub(1, #brandPrefix) == brandPrefix then
-- Return just the model code portion
-- Return just the model code portion
return accessoryName:sub(#brandPrefix + 1)
return itemName:sub(#brandPrefix + 1)
end
end
-- Otherwise return the full name
-- Otherwise return the full name
return accessoryName
return itemName
end
 
-- Helper to query inputs/outputs/control ports for a specific location
local function queryIOByLocation(frame, fullPageName, ioType, location)
local locationProp
if ioType == 'input' then
locationProp = 'Has input location'
elseif ioType == 'output' then
locationProp = 'Has output location'
else -- control
locationProp = 'Has control port location'
end
 
local locationFilter = ''
if location then
locationFilter = ' [[' .. locationProp .. '::' .. location .. ']]\n'
end
 
local signalProp, countProp, connectorProp, notesProp, labelProp, templateName
local extraProps = ''
 
if ioType == 'input' then
signalProp = 'Has video signal format'
countProp = 'Has input count'
connectorProp = 'Has connector type'
notesProp = 'Has input notes'
labelProp = 'Has input label'
templateName = 'CRT input row'
extraProps = ' |?Has audio type=audio\n' ..
' |?Has audio connector=audio_connector\n' ..
' |?Has audio shared with=audio_shared\n'
elseif ioType == 'output' then
signalProp = 'Has output signal format'
countProp = 'Has output count'
connectorProp = 'Has output connector'
notesProp = 'Has output notes'
labelProp = 'Has output label'
templateName = 'CRT output row'
else -- control
signalProp = 'Has control signal format'
countProp = 'Has control port count'
connectorProp = 'Has control port connector'
notesProp = 'Has control port notes'
labelProp = 'Has control port label'
templateName = 'CRT control port row'
extraProps = ' |?Has control port direction=direction\n'
end
 
local queryWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[' .. signalProp .. '::+]]\n' ..
locationFilter ..
' |?' .. countProp .. '=count\n' ..
' |?' .. signalProp .. '=signal\n' ..
' |?' .. connectorProp .. '=connector\n' ..
' |?' .. labelProp .. '=label\n' ..
' |?' .. notesProp .. '=notes\n' ..
extraProps ..
' |format=template\n' ..
' |template=' .. templateName .. '\n' ..
' |named args=yes\n' ..
' |link=none\n' ..
'}}'
 
local result = frame:preprocess(queryWikitext)
return trim(result)
end
 
-- Helper to discover all distinct location values for a given IO type
-- Returns an ordered list of location strings, sorted with common locations first
local function discoverLocations(frame, fullPageName, ioType)
local locationProp, signalProp
if ioType == 'input' then
locationProp = 'Has input location'
signalProp = 'Has video signal format'
elseif ioType == 'output' then
locationProp = 'Has output location'
signalProp = 'Has output signal format'
else -- control
locationProp = 'Has control port location'
signalProp = 'Has control signal format'
end
 
local queryWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[' .. signalProp .. '::+]]\n' ..
' [[' .. locationProp .. '::+]]\n' ..
' |?' .. locationProp .. '=location\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
 
local result = frame:preprocess(queryWikitext)
 
-- Parse distinct locations from broadtable rows
local seen = {}
local locations = {}
 
-- Preferred display order for common locations
local preferredOrder = {rear = 1, front = 2, side = 3, top = 4, bottom = 5, internal = 6}
 
if result then
for rowContent in result: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 name, cells[2] = location value
if #cells >= 2 and cells[2] then
local loc = mw.text.trim(cells[2])
-- Normalize to lowercase for deduplication
local locLower = loc:lower()
if locLower ~= '' and not seen[locLower] then
seen[locLower] = true
table.insert(locations, locLower)
end
end
end
end
 
-- Sort: preferred locations first (in defined order), then remaining alphabetically
table.sort(locations, function(a, b)
local orderA = preferredOrder[a] or 1000
local orderB = preferredOrder[b] or 1000
if orderA ~= orderB then
return orderA < orderB
end
return a < b
end)
 
return locations
end
 
-- Capitalize first letter of a location string for display
local function locationLabel(loc)
if not loc or loc == '' then return '' end
return loc:sub(1, 1):upper() .. loc:sub(2)
end
end


Line 87: Line 228:
local model_code = trim(args.model_code)
local model_code = trim(args.model_code)
local variants = trim(args.variants)
local variants = trim(args.variants)
local market = trim(args.market)
local series = trim(args.series)
local series = trim(args.series)
local image_main = trim(args.image_main)
local image_main = trim(args.image_main)
Line 155: Line 297:
'infobox-title'
'infobox-title'
)
)
-- Type badge (breadcrumb) — plainlinks hides external icon, $wgNoFollowDomainExceptions handles nofollow
local badgeWikitext = '[[CRT Search|CRT]]'
if crt_type then
local typeUrl = mw.title.new('CRT Search'):fullUrl({crt_type = crt_type})
badgeWikitext = badgeWikitext .. ' › <span class="plainlinks">[' .. typeUrl .. ' ' .. crt_type .. ']</span>'
end
addFullRow(container, badgeWikitext, '', false, 'infobox-type-badge')


if image_main then
if image_main then
Line 200: Line 350:
if variants then
if variants then
addRow(container, 'Variants', variants, false)
addRow(container, 'Variants', variants, false)
end
-- Market row
if market then
addRow(container, 'Market', market, false)
end
end


Line 279: Line 434:
-- Remote control row: use Accessory form, strip brand prefix if it matches CRT brand
-- Remote control row: use Accessory form, strip brand prefix if it matches CRT brand
if remote_control then
if remote_control then
local displayText = getAccessoryDisplayText(remote_control, brand)
local displayText = getDisplayTextWithoutBrand(remote_control, brand)
local remoteWikitext = string.format(
local remoteWikitext = string.format(
'{{#formredlink: target=%s |form=Accessory |existing page link text=%s }}',
'{{#formredlink: target=%s |form=Accessory |existing page link text=%s }}',
Line 418: Line 573:
if range.min and range.max then
if range.min and range.max then
if range.min == range.max then
if range.min == range.max then
-- Single voltage (e.g., "120V")
rangeStr = range.min .. 'V'
rangeStr = range.min .. 'V'
else
else
-- Range (e.g., "100–120V")
rangeStr = range.min .. '–' .. range.max .. 'V'
rangeStr = range.min .. '–' .. range.max .. 'V'
end
end
Line 435: Line 588:
if #rangeStrings > 0 then
if #rangeStrings > 0 then
-- Join multiple ranges with " / " (e.g., "100–120V / 220–240V")
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false)
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false)
end
end
Line 463: Line 615:
addSectionRow(container, 'TV Systems')
addSectionRow(container, 'TV Systems')
-- AV system row (baseband video color support)
if av_system then
if av_system then
-- Link each system to its wiki page
local systems = {}
local systems = {}
for system in av_system:gmatch('[^,]+') do
for system in av_system:gmatch('[^,]+') do
Line 478: Line 628:
end
end
-- Tuner system row (RF broadcast standards)
if tuner_system then
if tuner_system then
-- Link each system to its wiki page
local systems = {}
local systems = {}
for system in tuner_system:gmatch('[^,]+') do
for system in tuner_system:gmatch('[^,]+') do
Line 494: Line 642:
end
end


-- Scan Range section: show if we have any scan range specs
-- Scan Range section
if h_scan_min or h_scan_max or v_scan_min or v_scan_max then
if h_scan_min or h_scan_max or v_scan_min or v_scan_max then
addSectionRow(container, 'Scan Range')
addSectionRow(container, 'Scan Range')
-- Horizontal scan row
if h_scan_min or h_scan_max then
if h_scan_min or h_scan_max then
local hDisplay
local hDisplay
if h_scan_min and h_scan_max then
if h_scan_min and h_scan_max then
if h_scan_min == h_scan_max then
if h_scan_min == h_scan_max then
-- Single frequency (e.g., "15.7 kHz")
hDisplay = h_scan_min .. ' kHz'
hDisplay = h_scan_min .. ' kHz'
else
else
-- Range (e.g., "15–100 kHz")
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz'
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz'
end
end
Line 517: Line 662:
end
end
-- Vertical scan row
if v_scan_min or v_scan_max then
if v_scan_min or v_scan_max then
local vDisplay
local vDisplay
if v_scan_min and v_scan_max then
if v_scan_min and v_scan_max then
if v_scan_min == v_scan_max then
if v_scan_min == v_scan_max then
-- Single frequency (e.g., "60 Hz")
vDisplay = v_scan_min .. ' Hz'
vDisplay = v_scan_min .. ' Hz'
else
else
-- Range (e.g., "50–160 Hz")
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz'
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz'
end
end
Line 537: Line 679:
end
end


-- Inputs section: query ALL inputs with location shown inline
-- Inputs, Outputs, and Control Ports sections
local inputsCountWikitext =
local function renderIOSection(sectionTitle, ioType)
'{{#ask:\n' ..
local locations = discoverLocations(frame, fullPageName, ioType)
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
local locationData = {}
' [[Has video signal format::+]]\n' ..
 
' |format=count\n' ..
for _, loc in ipairs(locations) do
'}}'
local content = queryIOByLocation(frame, fullPageName, ioType, loc)
local inputCount = frame:preprocess(inputsCountWikitext)
if content and content ~= '' then
local hasInputs = inputCount and tonumber(trim(inputCount)) and tonumber(trim(inputCount)) > 0
table.insert(locationData, {location = loc, content = content})
end
if hasInputs then
end
local askWikitext =
 
'{{#ask:\n' ..
if #locationData > 0 then
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
addSectionRow(container, sectionTitle)
' [[Has video signal format::+]]\n' ..
for _, data in ipairs(locationData) do
' |?Has video signal format=signal\n' ..
local labelDiv = container:tag('div')
' |?Has connector type=connector\n' ..
labelDiv:addClass('infobox-label')
' |?Has input count=count\n' ..
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
' |?Has input location=location\n' ..
labelDiv:wikitext(locationLabel(data.location))
' |?Has audio type=audio\n' ..
 
' |?Has audio connector=audio_connector\n' ..
local valueDiv = container:tag('div')
' |?Has audio shared with=audio_shared\n' ..
valueDiv:addClass('infobox-value')
' |format=ul\n' ..
css(valueDiv, 'padding:0.2em 0.4em;')
' |template=CRT input inline\n' ..
valueDiv:node(mw.html.create('span'):wikitext(data.content))
' |named args=yes\n' ..
end
' |link=none\n' ..
end
' |searchlabel=\n' ..
'}}'
local askHtml = frame:preprocess(askWikitext)
addSectionRow(container, 'Inputs')
addFullRow(container, askHtml, 'padding:0.2em 0.4em;', true)
end
end


-- Outputs section: query ALL outputs with location shown inline
renderIOSection('Inputs', 'input')
local outputsCountWikitext =
renderIOSection('Outputs', 'output')
'{{#ask:\n' ..
renderIOSection('Control Ports', 'control')
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
 
' [[Has output signal format::+]]\n' ..
-- Accessories section
' |format=count\n' ..
addSectionRow(container, 'Accessories')
'}}'
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' ..
' |link=none\n' ..
' |searchlabel=\n' ..
'}}'
local outputsHtml = frame:preprocess(outputsAskWikitext)
addSectionRow(container, 'Outputs')
addFullRow(container, outputsHtml, 'padding:0.2em 0.4em;', true)
end


-- Control Ports section: query for control port subobjects
local addAccessoryUrl = mw.uri.fullUrl(
local controlPortsCountWikitext =
"Create an accessory",
'{{#ask:\n' ..
{
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
crt_models = pageName
' [[Has control signal format::+]]\n' ..
}
' |format=count\n' ..
)
'}}'
local controlPortCount = frame:preprocess(controlPortsCountWikitext)
local hasControlPorts = controlPortCount and tonumber(trim(controlPortCount)) and tonumber(trim(controlPortCount)) > 0
if hasControlPorts then
local controlPortsAskWikitext =
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has control signal format::+]]\n' ..
' |?Has control signal format=signal\n' ..
' |?Has control port connector=connector\n' ..
' |?Has control port direction=direction\n' ..
' |?Has control port count=count\n' ..
' |?Has control port label=label\n' ..
' |?Has control port location=location\n' ..
' |format=ul\n' ..
' |template=CRT control port inline\n' ..
' |named args=yes\n' ..
' |link=none\n' ..
' |searchlabel=\n' ..
'}}'
local controlPortsHtml = frame:preprocess(controlPortsAskWikitext)
addSectionRow(container, 'Control Ports')
addFullRow(container, controlPortsHtml, 'padding:0.2em 0.4em;', true)
end


-- Accessories section: Query for non-remote accessories linked via Has related CRT model
local accessoriesCountWikitext =
local accessoriesCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 647: Line 730:
local accessoryCount = frame:preprocess(accessoriesCountWikitext)
local accessoryCount = frame:preprocess(accessoriesCountWikitext)
local hasAccessories = accessoryCount and tonumber(trim(accessoryCount)) and tonumber(trim(accessoryCount)) > 0
local hasAccessories = accessoryCount and tonumber(trim(accessoryCount)) and tonumber(trim(accessoryCount)) > 0
 
local accDiv = container:tag('div')
css(accDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
 
if hasAccessories then
if hasAccessories then
addSectionRow(container, 'Accessories')
local accessoriesDiv = container:tag('div')
css(accessoriesDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
-- Query accessories and display as a list
local accessoriesQueryWikitext =
local accessoriesQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 662: Line 742:
' |?Has accessory type=type\n' ..
' |?Has accessory type=type\n' ..
' |?Has model code=model\n' ..
' |?Has model code=model\n' ..
' |?Has description=desc\n' ..
' |format=broadtable\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |headers=hide\n' ..
Line 668: Line 747:
'}}'
'}}'
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
 
-- Parse accessories from broadtable result
local accessoriesByType = {}
local accessories = {}
local typeOrder = {}
 
if accessoriesResult then
if accessoriesResult then
for rowContent in accessoriesResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in accessoriesResult:gmatch('<tr[^>]*>(.-)</tr>') do
Line 678: Line 758:
table.insert(cells, trim(cleanContent))
table.insert(cells, trim(cleanContent))
end
end
 
-- cells[1] = page name, cells[2] = type, cells[3] = model code, cells[4] = description
if #cells >= 2 then
if #cells >= 2 then
local accPageName = cells[1]
local accPageName = cells[1]
local accType = cells[2]
local accType = cells[2] or 'Accessory'
local accModel = cells[3]
 
local accDesc = cells[4]
if accPageName then
if accPageName then
table.insert(accessories, {
if not accessoriesByType[accType] then
page = accPageName,
accessoriesByType[accType] = {}
type = accType,
table.insert(typeOrder, accType)
model = accModel,
end
desc = accDesc
 
table.insert(accessoriesByType[accType], {
page = accPageName
})
})
end
end
Line 697: Line 776:
end
end
end
end
local accGrid = accDiv:tag('div')
css(accGrid, 'display:grid; grid-template-columns:auto 1fr; gap:3px;')
for _, accType in ipairs(typeOrder) do
local accs = accessoriesByType[accType]
if accs and #accs > 0 then
local accLinks = {}
for _, acc in ipairs(accs) do
table.insert(accLinks, '[[' .. acc.page .. ']]')
end
local labelDiv = accGrid:tag('div')
labelDiv:addClass('infobox-label')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(accType)
local valueDiv = accGrid:tag('div')
valueDiv:addClass('infobox-value')
css(valueDiv, 'padding:0.2em 0.4em;')
valueDiv:wikitext(table.concat(accLinks, '<br>'))
end
end
local addAccDiv = mw.html.create('div')
addAccDiv:css('margin-top', '0.5em'):css('text-align', 'center')
addAccDiv:wikitext('[' .. tostring(addAccessoryUrl) .. ' <span class="infobox-button infobox-button-secondary">Add accessory</span>]')
accDiv:node(addAccDiv)
else
local addAccDiv = mw.html.create('div')
addAccDiv:css('text-align', 'center')
addAccDiv:wikitext('[' .. tostring(addAccessoryUrl) .. ' <span class="infobox-button infobox-button-secondary">Add accessory</span>]')
accDiv:node(addAccDiv)
end
-- Parts section
addSectionRow(container, 'Parts')
local addPartUrl = mw.uri.fullUrl(
"Create a part",
{
crt_models = pageName
}
)
local partsCountWikitext =
'{{#ask:\n' ..
' [[Category:Parts]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |format=count\n' ..
'}}'
local partsCount = frame:preprocess(partsCountWikitext)
local hasParts = partsCount and tonumber(trim(partsCount)) and tonumber(trim(partsCount)) > 0
local partsDiv = container:tag('div')
css(partsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
if hasParts then
local partsQueryWikitext =
'{{#ask:\n' ..
' [[Category:Parts]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |?Has part type=type\n' ..
' |?Has part subtype=subtype\n' ..
' |?Has model code=model\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
local partsResult = frame:preprocess(partsQueryWikitext)
if #accessories > 0 then
local partsByType = {}
local ul = accessoriesDiv:tag('ul')
local partTypeOrder = {}
ul:css('margin', '0'):css('padding-left', '1.5em')
if partsResult then
for _, acc in ipairs(accessories) do
for rowContent in partsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local li = ul:tag('li')
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
-- Get display text: strip brand prefix if it matches CRT brand
if #cells >= 2 then
local displayText = getAccessoryDisplayText(acc.page, brand)
local partPageName = cells[1]
local partType = cells[2] or 'Part'
local partSubtype = cells[3]
local groupKey = partType
if partSubtype and partSubtype ~= '' then
groupKey = partSubtype .. ' ' .. partType
end
if partPageName then
if not partsByType[groupKey] then
partsByType[groupKey] = {}
table.insert(partTypeOrder, groupKey)
end
table.insert(partsByType[groupKey], {
page = partPageName
})
end
end
end
end
local partsGrid = partsDiv:tag('div')
css(partsGrid, 'display:grid; grid-template-columns:auto 1fr; gap:3px;')
for _, partType in ipairs(partTypeOrder) do
local parts = partsByType[partType]
if parts and #parts > 0 then
local partLinks = {}
for _, part in ipairs(parts) do
table.insert(partLinks, '[[' .. part.page .. ']]')
end
-- Build the display: model code (optionally with description)
local labelDiv = partsGrid:tag('div')
local linkText = displayText
labelDiv:addClass('infobox-label')
if acc.desc and acc.desc ~= '' then
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
linkText = displayText .. ' – ' .. acc.desc
labelDiv:wikitext(partType)
end
-- Add type prefix in small text
local valueDiv = partsGrid:tag('div')
if acc.type then
valueDiv:addClass('infobox-value')
li:wikitext('<small>' .. acc.type .. ':</small> [[' .. acc.page .. '|' .. linkText .. ']]')
css(valueDiv, 'padding:0.2em 0.4em;')
else
valueDiv:wikitext(table.concat(partLinks, '<br>'))
li:wikitext('[[' .. acc.page .. '|' .. linkText .. ']]')
end
end
end
end
end
local addPartDiv = mw.html.create('div')
addPartDiv:css('margin-top', '0.5em'):css('text-align', 'center')
addPartDiv:wikitext('[' .. tostring(addPartUrl) .. ' <span class="infobox-button infobox-button-secondary">Add part</span>]')
partsDiv:node(addPartDiv)
else
local addPartDiv = mw.html.create('div')
addPartDiv:css('text-align', 'center')
addPartDiv:wikitext('[' .. tostring(addPartUrl) .. ' <span class="infobox-button infobox-button-secondary">Add part</span>]')
partsDiv:node(addPartDiv)
end
end


-- Mods section: Query for mod subobjects and display
-- Mods section
local modsQueryWikitext =
local modsQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 739: Line 931:
local modsResult = frame:preprocess(modsQueryWikitext)
local modsResult = frame:preprocess(modsQueryWikitext)
-- Parse mods from broadtable result
local mods = {}
local mods = {}
Line 780: Line 971:
end
end
-- Display mods section if there are any
if #mods > 0 then
if #mods > 0 then
addSectionRow(container, 'Mods')
addSectionRow(container, 'Mods')
Line 793: Line 983:
local li = ul:tag('li')
local li = ul:tag('li')
-- Determine display text: use description if available, otherwise type
local displayText = mod.description or mod.type
local displayText = mod.description or mod.type
-- Check if mod is not feasible
local isFeasible = mod.feasible ~= 'No'
local isFeasible = mod.feasible ~= 'No'
if isFeasible then
if isFeasible then
-- Normal link for feasible mods using wikitext external link
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']')
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']')
else
else
-- Red styling with X for impossible mods
local wrapper = li:tag('span')
local wrapper = li:tag('span')
wrapper:addClass('mod-not-feasible')
wrapper:addClass('mod-not-feasible')
Line 817: Line 1,002:
addSectionRow(container, 'Documents')
addSectionRow(container, 'Documents')
-- Build the upload link with pseudorandom unique identifier
local timestamp = os.time()
local timestamp = os.time()
math.randomseed(timestamp + pageId) -- Seed with timestamp + page ID for better randomness
math.randomseed(timestamp + pageId)
local random = math.random(1000, 9999)
local random = math.random(1000, 9999)
local target = "Document:" .. timestamp .. "-" .. random
local target = "Document:" .. timestamp .. "-" .. random
Line 831: Line 1,015:
)
)
-- Query for document count
local documentsCountWikitext =
local documentsCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 841: Line 1,024:
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 div
local docsDiv = container:tag('div')
local docsDiv = container:tag('div')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
if hasDocuments then
if hasDocuments then
-- Use #ask to get documents
local docsQueryWikitext =
local docsQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 861: Line 1,042:
local docsResult = frame:preprocess(docsQueryWikitext)
local docsResult = frame:preprocess(docsQueryWikitext)
-- Create UL element
local ul = mw.html.create('ul')
local ul = mw.html.create('ul')
ul:css('margin', '0'):css('padding-left', '1.5em')
ul:css('margin', '0'):css('padding-left', '1.5em')
-- Parse the HTML table - extract data from <td> tags
if docsResult then
if docsResult then
-- Match each table row
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, cleanContent)
table.insert(cells, cleanContent)
end
end
-- cells[1] = page name, cells[2] = type, cells[3] = URL, cells[4] = file, cells[5] = title
if #cells >= 2 then
if #cells >= 2 then
local docType = trim(cells[2])
local docType = trim(cells[2])
Line 884: Line 1,059:
local docTitle = trim(cells[5])
local docTitle = trim(cells[5])
-- Determine what to link to (URL or file)
local linkTarget = nil
local linkTarget = nil
if docUrl then
if docUrl then
linkTarget = docUrl
linkTarget = docUrl
elseif docFile then
elseif docFile then
-- Extract filename from "File:..." format if present
local filename = docFile:match('File:(.+)') or docFile
local filename = docFile:match('File:(.+)') or docFile
linkTarget = tostring(mw.uri.fullUrl('File:' .. filename))
linkTarget = tostring(mw.uri.fullUrl('File:' .. filename))
end
end


-- Always link to the wiki page
local docPageName = trim(cells[1])
local docPageName = trim(cells[1])
if docPageName then
if docPageName then
-- Determine display text priority: custom title > type > default
local displayText = 'Document'
local displayText = 'Document'
if docTitle then
if docTitle then
Line 906: Line 1,077:
end
end
-- Create list item with link to wiki page
local li = ul:tag('li')
local li = ul:tag('li')
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]')
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]')
Line 916: Line 1,086:
docsDiv:node(ul)
docsDiv:node(ul)
-- 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 class="infobox-button infobox-button-secondary">Add a document</span>]')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]')
docsDiv:node(uploadDiv)
docsDiv:node(uploadDiv)
else
else
-- 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 class="infobox-button infobox-button-secondary">Add a document</span>]')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]')
docsDiv:node(uploadDiv)
docsDiv:node(uploadDiv)
end
end


-- Links section: Query for link subobjects and display by type
-- Links section
local linksQueryWikitext =
local linksQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 944: Line 1,112:
local linksResult = frame:preprocess(linksQueryWikitext)
local linksResult = frame:preprocess(linksQueryWikitext)
-- Parse links and organize by type
local linksByType = {
local linksByType = {
Manufacturer = {},
Manufacturer = {},
Line 953: Line 1,120:
if linksResult then
if linksResult then
-- Match each table row
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
table.insert(cells, trim(cleanContent))
end
end
-- cells[1] = page/subobject, cells[2] = type, cells[3] = source, cells[4] = URL
if #cells >= 4 then
if #cells >= 4 then
local linkType = cells[2]
local linkType = cells[2]
Line 979: Line 1,142:
end
end
-- Display links section if there are any links
if hasLinks then
if hasLinks then
addSectionRow(container, 'Archival Links')
addSectionRow(container, 'Archival Links')
-- Add links in order: Database, Manufacturer, Retailer
local linkTypeOrder = {'Database', 'Manufacturer', 'Retailer'}
local typeOrder = {'Database', 'Manufacturer', 'Retailer'}
for _, linkType in ipairs(linkTypeOrder) do
for _, linkType in ipairs(typeOrder) do
if #linksByType[linkType] > 0 then
if #linksByType[linkType] > 0 then
-- If multiple links of the same type, combine them in one row
local linksHtml = {}
local linksHtml = {}
for _, link in ipairs(linksByType[linkType]) do
for _, link in ipairs(linksByType[linkType]) do
Line 1,000: Line 1,160:
addSectionRow(container, 'Contribute')
addSectionRow(container, 'Contribute')
-- Build the edit link URL
local editUrl = mw.uri.fullUrl(
local editUrl = mw.uri.fullUrl(
"Special:FormStart",
"Special:FormStart",
Line 1,009: Line 1,168:
)
)
-- Create the edit button div
local editDiv = container:tag('div')
local editDiv = container:tag('div')
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
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>]')
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span class="infobox-button infobox-button-primary">Edit this data</span>]')


-- Return templatestyles tag + container HTML
return styleTag .. tostring(container)
return styleTag .. tostring(container)
end
end


return p
return p
MediaWiki Appliance - Powered by TurnKey Linux