Module:AVDevice: Difference between revisions
Remove random timestamp targets from Add Part and Add Accessory buttons — let users name pages themselves (via update-page on MediaWiki MCP Server) |
Restore pre-compression source from rev 8199 (with comments, formatting, safe gsub patterns). Only change: args.oem_manufacturer → args.oem (the intended rename from rev 8313, without the destructive compression) (via update-page on MediaWiki MCP Server) |
||
| (15 intermediate revisions by the same user not shown) | |||
| Line 108: | Line 108: | ||
end | end | ||
return '$' .. formatted | return '$' .. formatted | ||
end | |||
-- Helper to format related models as wiki links, stripping brand prefix | |||
local function formatRelatedModels(value, deviceBrand) | |||
if not value then return nil end | |||
local items = {} | |||
for item in value:gmatch('[^,]+') do | |||
local trimmed = trim(item) | |||
if trimmed then | |||
local displayText = getDisplayTextWithoutBrand(trimmed, deviceBrand) | |||
table.insert(items, '[[' .. trimmed .. '|' .. displayText .. ']]') | |||
end | |||
end | |||
if #items == 0 then return nil end | |||
return table.concat(items, '<br>') | |||
end | end | ||
| Line 153: | Line 168: | ||
end | end | ||
-- Helper to | -- Helper to discover all distinct location values for a given IO type | ||
local function | -- Returns an ordered list of location strings, sorted with common locations first | ||
local | local function discoverLocations(frame, fullPageName, ioType) | ||
local locationProp, signalProp | |||
if ioType == 'input' then | |||
locationProp = 'Has input location' | |||
signalProp = 'Has video signal format' | |||
else -- output | |||
locationProp = 'Has output location' | |||
signalProp = 'Has output signal format' | |||
end | end | ||
local queryWikitext = | |||
local | |||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
' [[Has AV device::' .. fullPageName .. ']]\n' .. | ' [[Has AV device::' .. fullPageName .. ']]\n' .. | ||
' [[' .. signalProp .. '::+]]\n' .. | ' [[' .. signalProp .. '::+]]\n' .. | ||
' [[' .. locationProp .. '::+]]\n' .. | |||
' |format= | ' |?' .. locationProp .. '=location\n' .. | ||
' |format=broadtable\n' .. | |||
' |headers=hide\n' .. | |||
' |link=none\n' .. | |||
'}}' | '}}' | ||
-- | local result = frame:preprocess(queryWikitext) | ||
-- | |||
local | -- 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 | 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 | end | ||
end | end | ||
if | -- Sort: preferred locations first (in defined order), then remaining alphabetically | ||
return | 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 210: | Line 251: | ||
local brand = trim(args.brand) | local brand = trim(args.brand) | ||
local model_code = trim(args.model_code) | local model_code = trim(args.model_code) | ||
local oem_manufacturer = trim(args.oem) | |||
local device_type = trim(args.device_type) | local device_type = trim(args.device_type) | ||
local format_family = trim(args.format_family) | local format_family = trim(args.format_family) | ||
local market = trim(args.market) | |||
local supported_formats = trim(args.supported_formats) | local supported_formats = trim(args.supported_formats) | ||
local recording_formats = trim(args.recording_formats) | local recording_formats = trim(args.recording_formats) | ||
| Line 228: | Line 271: | ||
local depth = trim(args.depth) | local depth = trim(args.depth) | ||
local weight = trim(args.weight) | local weight = trim(args.weight) | ||
local related_models = trim(args.related_models) | |||
-- Use frame:extensionTag to create a proper <style> tag | -- Use frame:extensionTag to create a proper <style> tag | ||
| Line 245: | Line 289: | ||
'infobox-title' | 'infobox-title' | ||
) | ) | ||
-- Type badge (breadcrumb) — plainlinks hides external icon, $wgNoFollowDomainExceptions handles nofollow | |||
local badgeWikitext = '[[AV Device Search|AV device]]' | |||
if device_type then | |||
local typeUrl = tostring(mw.uri.fullUrl('AV Device Search', {device_type = device_type})) | |||
badgeWikitext = badgeWikitext .. ' › <span class="plainlinks">[' .. typeUrl .. ' ' .. device_type .. ']</span>' | |||
end | |||
addFullRow(container, badgeWikitext, '', false, 'infobox-type-badge') | |||
if image_main then | if image_main then | ||
| Line 267: | Line 319: | ||
local brandHtml = frame:preprocess(brandWikitext) | local brandHtml = frame:preprocess(brandWikitext) | ||
addRow(container, 'Brand', brandHtml, true) | addRow(container, 'Brand', brandHtml, true) | ||
end | |||
-- OEM row (shown only when set) | |||
if oem_manufacturer then | |||
local oemWikitext = string.format( | |||
'{{#formredlink: target=%s |form=Manufacturer |existing page link text=%s }}', | |||
oem_manufacturer, oem_manufacturer | |||
) | |||
local oemHtml = frame:preprocess(oemWikitext) | |||
addRow(container, 'OEM', oemHtml, true) | |||
end | end | ||
| Line 273: | Line 335: | ||
end | end | ||
-- Market row | |||
if market then | |||
addRow(container, 'Market', market, false) | |||
end | |||
-- Type row: link to AV Device Search with filter, not to a (possibly non-existent) article | |||
if device_type then | if device_type then | ||
local typeSearchUrl = tostring(mw.uri.fullUrl('AV Device Search', {device_type = device_type})) | |||
local typeWikitext = '<span class="plainlinks">[' .. typeSearchUrl .. ' ' .. device_type .. ']</span>' | |||
addRow(container, 'Type', typeWikitext, true) | |||
end | end | ||
| Line 305: | Line 375: | ||
if formattedAV then | if formattedAV then | ||
addRow(container, 'AV system', formattedAV, false) | addRow(container, 'AV system', formattedAV, false) | ||
end | |||
end | |||
-- Related models | |||
if related_models then | |||
local formattedRelated = formatRelatedModels(related_models, brand) | |||
if formattedRelated then | |||
addRow(container, 'Related', formattedRelated, true) | |||
end | end | ||
end | end | ||
| Line 331: | Line 409: | ||
end | end | ||
- | -- Inputs and Outputs sections (matching CRT model style) | ||
local function renderIOSection(sectionTitle, ioType) | |||
local | local locations = discoverLocations(frame, fullPageName, ioType) | ||
local locationData = {} | |||
for _, loc in ipairs(locations) do | |||
local content = queryIOByLocation(frame, fullPageName, ioType, loc) | |||
if content and content ~= '' then | |||
table.insert(locationData, {location = loc, content = content}) | |||
end | |||
end | |||
local | |||
if #locationData > 0 then | |||
addSectionRow(container, sectionTitle) | |||
for _, data in ipairs(locationData) do | |||
local labelDiv = container:tag('div') | |||
labelDiv:addClass('infobox-label') | |||
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;') | |||
labelDiv:wikitext(locationLabel(data.location)) | |||
local valueDiv = container:tag('div') | |||
valueDiv:addClass('infobox-value') | |||
css(valueDiv, 'padding:0.2em 0.4em;') | |||
valueDiv:node(mw.html.create('span'):wikitext(data.content)) | |||
end | |||
end | end | ||
end | end | ||
renderIOSection('Inputs', 'input') | |||
renderIOSection('Outputs', 'output') | |||
-- Features section | -- Features section | ||
| Line 527: | Line 598: | ||
addSectionRow(container, 'Accessories') | addSectionRow(container, 'Accessories') | ||
-- Build the add accessory link with | -- Build the add accessory link pointing to Create_an_accessory with av_devices parameter | ||
local addAccessoryUrl = mw.uri.fullUrl( | local addAccessoryUrl = mw.uri.fullUrl( | ||
" | "Create an accessory", | ||
{ | { | ||
av_devices = pageName | |||
} | } | ||
) | ) | ||
-- Query for | -- Query for accessories linked via Has related AV device | ||
-- Unlike CRT models which have a dedicated remote_control field, | |||
-- AV devices show all accessory types including remote controls here. | |||
local accessoriesCountWikitext = | local accessoriesCountWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
' [[Category:Accessories]]\n' .. | ' [[Category:Accessories]]\n' .. | ||
' [[Has related AV device::' .. fullPageName .. ']]\n' .. | ' [[Has related AV device::' .. fullPageName .. ']]\n' .. | ||
' |format=count\n' .. | ' |format=count\n' .. | ||
'}}' | '}}' | ||
| Line 556: | Line 627: | ||
' [[Category:Accessories]]\n' .. | ' [[Category:Accessories]]\n' .. | ||
' [[Has related AV device::' .. fullPageName .. ']]\n' .. | ' [[Has related AV device::' .. fullPageName .. ']]\n' .. | ||
' |?Has accessory type=type\n' .. | ' |?Has accessory type=type\n' .. | ||
' |?Has model code=model\n' .. | ' |?Has model code=model\n' .. | ||
| Line 637: | Line 707: | ||
addSectionRow(container, 'Parts') | addSectionRow(container, 'Parts') | ||
-- Build the add part link with | -- Build the add part link pointing to Create_a_part with av_devices parameter | ||
local addPartUrl = mw.uri.fullUrl( | local addPartUrl = mw.uri.fullUrl( | ||
" | "Create a part", | ||
{ | { | ||
av_devices = pageName | |||
} | } | ||
) | ) | ||
| Line 693: | Line 762: | ||
-- Use subtype + type as the grouping key if subtype exists | -- Use subtype + type as the grouping key if subtype exists | ||
local groupKey = partType | local groupKey = partType | ||
if partSubtype and partSubtype ~= '' then | if partSubtype and partSubtype ~= '' then | ||
| Line 724: | Line 792: | ||
local partLinks = {} | local partLinks = {} | ||
for _, part in ipairs(parts) do | for _, part in ipairs(parts) do | ||
table.insert(partLinks, '[[' .. part.page .. ']]') | table.insert(partLinks, '[[' .. part.page .. ']]') | ||
end | end | ||
| Line 870: | Line 937: | ||
if #cells >= 2 then | if #cells >= 2 then | ||
local docType = trim(cells[2]) | local docType = trim(cells[2]) | ||
local docTitle = trim(cells[5]) | local docTitle = trim(cells[5]) | ||
local docPageName = trim(cells[1]) | local docPageName = trim(cells[1]) | ||