Module:CRTModel: Difference between revisions

Refactor Connectivity section: split into separate Inputs/Outputs/Control Ports sections with location (Front/Rear/Side) as row labels instead of sub-groups (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)
 
(21 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
end


Line 142: Line 142:
end
end


-- Helper to count inputs/outputs/control ports for a specific location
-- Helper to discover all distinct location values for a given IO type
local function countIOByLocation(frame, fullPageName, ioType, location)
-- Returns an ordered list of location strings, sorted with common locations first
local locationProp
local function discoverLocations(frame, fullPageName, ioType)
local locationProp, signalProp
if ioType == 'input' then
if ioType == 'input' then
locationProp = 'Has input location'
locationProp = 'Has input location'
signalProp = 'Has video signal format'
elseif ioType == 'output' then
elseif ioType == 'output' then
locationProp = 'Has output location'
locationProp = 'Has output location'
signalProp = 'Has output signal format'
else -- control
else -- control
locationProp = 'Has control port location'
locationProp = 'Has control port location'
end
local locationFilter = ''
if location then
locationFilter = ' [[' .. locationProp .. '::' .. location .. ']]\n'
end
local signalProp
if ioType == 'input' then
signalProp = 'Has video signal format'
elseif ioType == 'output' then
signalProp = 'Has output signal format'
else
signalProp = 'Has control signal format'
signalProp = 'Has control signal format'
end
end


local countWikitext =
local queryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[Has CRT model::' .. fullPageName .. ']]\n' ..
' [[' .. signalProp .. '::+]]\n' ..
' [[' .. signalProp .. '::+]]\n' ..
locationFilter ..
' [[' .. locationProp .. '::+]]\n' ..
' |format=count\n' ..
' |?' .. locationProp .. '=location\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
'}}'


local result = frame:preprocess(countWikitext)
local result = frame:preprocess(queryWikitext)
return tonumber(trim(result)) or 0
 
end
-- Parse distinct locations from broadtable rows
local seen = {}
local locations = {}


-- Helper to build location-grouped I/O content (used by Module:AVDevice pattern)
-- Preferred display order for common locations
-- Returns HTML string with stripe groups per location, or nil if empty
local preferredOrder = {rear = 1, front = 2, side = 3, top = 4, bottom = 5, internal = 6}
local function buildLocationGroupedIO(frame, fullPageName, ioType, locations, locationLabels)
local html = ''


for _, loc in ipairs(locations) do
if result then
local count = countIOByLocation(frame, fullPageName, ioType, loc)
for rowContent in result:gmatch('<tr[^>]*>(.-)</tr>') do
if count > 0 then
local cells = {}
local locContent = queryIOByLocation(frame, fullPageName, ioType, loc)
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
if locContent and locContent ~= '' then
local cleanContent = cellContent:gsub('<[^>]+>', '')
html = html ..
table.insert(cells, trim(cleanContent))
'<div class="infobox-io-location-group">' ..
end
'<div class="infobox-io-location-header">' .. locationLabels[loc] .. '</div>' ..
-- cells[1] = subobject name, cells[2] = location value
'<div class="infobox-io-location-content">' .. locContent .. '</div>' ..
if #cells >= 2 and cells[2] then
'</div>'
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
end
end
end


if html == '' then return nil end
-- Sort: preferred locations first (in defined order), then remaining alphabetically
return html
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 214: 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 282: 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 327: 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 406: 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 545: 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 562: 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 590: 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 605: 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 621: 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 644: 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 664: Line 679:
end
end


-- Inputs, Outputs, and Control Ports as separate sections
-- Inputs, Outputs, and Control Ports sections
-- Each section uses location (Front/Rear/Side) as the row label
local locations = {'front', 'rear', 'side'}
local locationLabels = {front = 'Front', rear = 'Rear', side = 'Side'}
 
-- Helper closure to render an I/O section with location-based rows
local function renderIOSection(sectionTitle, ioType)
local function renderIOSection(sectionTitle, ioType)
local locations = discoverLocations(frame, fullPageName, ioType)
local locationData = {}
local locationData = {}


for _, loc in ipairs(locations) do
for _, loc in ipairs(locations) do
local count = countIOByLocation(frame, fullPageName, ioType, loc)
local content = queryIOByLocation(frame, fullPageName, ioType, loc)
if count > 0 then
if content and content ~= '' then
local content = queryIOByLocation(frame, fullPageName, ioType, loc)
table.insert(locationData, {location = loc, content = content})
if content and content ~= '' then
table.insert(locationData, {location = loc, content = content})
end
end
end
end
end
Line 688: Line 696:
local labelDiv = container:tag('div')
local labelDiv = container:tag('div')
labelDiv:addClass('infobox-label')
labelDiv:addClass('infobox-label')
labelDiv:addClass('infobox-label-top')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(locationLabels[data.location])
labelDiv:wikitext(locationLabel(data.location))


local valueDiv = container:tag('div')
local valueDiv = container:tag('div')
Line 704: Line 711:
renderIOSection('Control Ports', 'control')
renderIOSection('Control Ports', 'control')


-- Accessories section: Query for non-remote accessories linked via Has related CRT model
-- Accessories section
addSectionRow(container, 'Accessories')
 
local addAccessoryUrl = mw.uri.fullUrl(
"Create an accessory",
{
crt_models = pageName
}
)
 
local accessoriesCountWikitext =
local accessoriesCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 714: 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')
-- Query accessories and display grouped by type
local accessoriesQueryWikitext =
local accessoriesQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 726: 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 732: Line 747:
'}}'
'}}'
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
 
-- Parse accessories from broadtable result and group by type
local accessoriesByType = {}
local accessoriesByType = {}
local typeOrder = {}
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 744: 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] or 'Accessory'
local accType = cells[2] or 'Accessory'
local accDesc = cells[4]
 
if accPageName then
if accPageName then
-- Track type order for consistent display
if not accessoriesByType[accType] then
if not accessoriesByType[accType] then
accessoriesByType[accType] = {}
accessoriesByType[accType] = {}
table.insert(typeOrder, accType)
table.insert(typeOrder, accType)
end
end
 
table.insert(accessoriesByType[accType], {
table.insert(accessoriesByType[accType], {
page = accPageName,
page = accPageName
desc = accDesc
})
})
end
end
Line 766: Line 776:
end
end
end
end
 
-- Display as label/value rows grouped by type (like Archival Links)
local accGrid = accDiv:tag('div')
css(accGrid, 'display:grid; grid-template-columns:auto 1fr; gap:3px;')
 
for _, accType in ipairs(typeOrder) do
for _, accType in ipairs(typeOrder) do
local accs = accessoriesByType[accType]
local accs = accessoriesByType[accType]
Line 773: Line 785:
local accLinks = {}
local accLinks = {}
for _, acc in ipairs(accs) do
for _, acc in ipairs(accs) do
local displayText = getAccessoryDisplayText(acc.page, brand)
table.insert(accLinks, '[[' .. acc.page .. ']]')
local linkText = displayText
end
if acc.desc and acc.desc ~= '' then
 
linkText = displayText .. ' ' .. acc.desc
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)
local partsByType = {}
local partTypeOrder = {}
if partsResult then
for rowContent in partsResult: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 >= 2 then
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
table.insert(accLinks, '[[' .. acc.page .. '|' .. linkText .. ']]')
end
end
addRow(container, accType, table.concat(accLinks, '<br>'), true)
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
local labelDiv = partsGrid:tag('div')
labelDiv:addClass('infobox-label')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(partType)
local valueDiv = partsGrid:tag('div')
valueDiv:addClass('infobox-value')
css(valueDiv, 'padding:0.2em 0.4em;')
valueDiv:wikitext(table.concat(partLinks, '<br>'))
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 800: Line 931:
local modsResult = frame:preprocess(modsQueryWikitext)
local modsResult = frame:preprocess(modsQueryWikitext)
-- Parse mods from broadtable result
local mods = {}
local mods = {}
Line 841: 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 854: 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 878: 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 892: Line 1,015:
)
)
-- Query for document count
local documentsCountWikitext =
local documentsCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 902: 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 922: 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 945: 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 967: 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 977: 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 1,005: 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 1,014: 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 1,040: 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,061: 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,070: 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