Module:CRTModel: Difference between revisions
Dynamic location discovery for IO sections - no more hardcoded front/rear/side list. Discovers all locations from semantic data and sorts with preferred order (rear, front, side first, then alphabetical). (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) |
||
| (19 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 | local function getDisplayTextWithoutBrand(itemName, deviceBrand) | ||
if not | if not itemName then return nil end | ||
if not | if not deviceBrand then return itemName end | ||
-- Check if | -- Check if item name starts with the device brand followed by a space | ||
local brandPrefix = | local brandPrefix = deviceBrand .. ' ' | ||
if | if itemName:sub(1, #brandPrefix) == brandPrefix then | ||
-- Return just the model code portion | -- Return just the model code portion | ||
return | return itemName:sub(#brandPrefix + 1) | ||
end | end | ||
-- Otherwise return the full name | -- Otherwise return the full name | ||
return | return itemName | ||
end | end | ||
| Line 228: | 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 296: | 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 341: | 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 420: | 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 = | 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 559: | 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 | ||
rangeStr = range.min .. 'V' | rangeStr = range.min .. 'V' | ||
else | else | ||
rangeStr = range.min .. '–' .. range.max .. 'V' | rangeStr = range.min .. '–' .. range.max .. 'V' | ||
end | end | ||
| Line 576: | Line 588: | ||
if #rangeStrings > 0 then | if #rangeStrings > 0 then | ||
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false) | addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false) | ||
end | end | ||
| Line 604: | Line 615: | ||
addSectionRow(container, 'TV Systems') | addSectionRow(container, 'TV Systems') | ||
if av_system then | if av_system then | ||
local systems = {} | local systems = {} | ||
for system in av_system:gmatch('[^,]+') do | for system in av_system:gmatch('[^,]+') do | ||
| Line 619: | Line 628: | ||
end | end | ||
if tuner_system then | if tuner_system then | ||
local systems = {} | local systems = {} | ||
for system in tuner_system:gmatch('[^,]+') do | for system in tuner_system:gmatch('[^,]+') do | ||
| Line 635: | Line 642: | ||
end | end | ||
-- Scan Range section | -- 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') | ||
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 | ||
hDisplay = h_scan_min .. ' kHz' | hDisplay = h_scan_min .. ' kHz' | ||
else | else | ||
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz' | hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz' | ||
end | end | ||
| Line 658: | Line 662: | ||
end | end | ||
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 | ||
vDisplay = v_scan_min .. ' Hz' | vDisplay = v_scan_min .. ' Hz' | ||
else | else | ||
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz' | vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz' | ||
end | end | ||
| Line 679: | Line 680: | ||
-- Inputs, Outputs, and Control Ports sections | -- Inputs, Outputs, and Control Ports sections | ||
local function renderIOSection(sectionTitle, ioType) | local function renderIOSection(sectionTitle, ioType) | ||
local locations = discoverLocations(frame, fullPageName, ioType) | local locations = discoverLocations(frame, fullPageName, ioType) | ||
| Line 713: | Line 711: | ||
renderIOSection('Control Ports', 'control') | renderIOSection('Control Ports', 'control') | ||
-- Accessories section | -- 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 723: | 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 | ||
local accessoriesQueryWikitext = | local accessoriesQueryWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
| Line 735: | Line 742: | ||
' |?Has accessory type=type\n' .. | ' |?Has accessory type=type\n' .. | ||
' |?Has model code=model\n' .. | ' |?Has model code=model\n' .. | ||
' |format=broadtable\n' .. | ' |format=broadtable\n' .. | ||
' |headers=hide\n' .. | ' |headers=hide\n' .. | ||
| Line 741: | Line 747: | ||
'}}' | '}}' | ||
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext) | local accessoriesResult = frame:preprocess(accessoriesQueryWikitext) | ||
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 753: | Line 758: | ||
table.insert(cells, trim(cleanContent)) | table.insert(cells, trim(cleanContent)) | ||
end | end | ||
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' | ||
if accPageName then | if accPageName then | ||
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 | ||
}) | }) | ||
end | end | ||
| Line 775: | 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 | for _, accType in ipairs(typeOrder) do | ||
local accs = accessoriesByType[accType] | local accs = accessoriesByType[accType] | ||
| Line 782: | Line 785: | ||
local accLinks = {} | local accLinks = {} | ||
for _, acc in ipairs(accs) do | for _, acc in ipairs(accs) do | ||
local | table.insert(accLinks, '[[' .. acc.page .. ']]') | ||
local | end | ||
if | |||
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 | end | ||
table.insert( | |||
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 | 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 | ||
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 | -- Mods section | ||
local modsQueryWikitext = | local modsQueryWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
| Line 809: | Line 931: | ||
local modsResult = frame:preprocess(modsQueryWikitext) | local modsResult = frame:preprocess(modsQueryWikitext) | ||
local mods = {} | local mods = {} | ||
| Line 850: | Line 971: | ||
end | end | ||
if #mods > 0 then | if #mods > 0 then | ||
addSectionRow(container, 'Mods') | addSectionRow(container, 'Mods') | ||
| Line 863: | Line 983: | ||
local li = ul:tag('li') | local li = ul:tag('li') | ||
local displayText = mod.description or mod.type | local displayText = mod.description or mod.type | ||
local isFeasible = mod.feasible ~= 'No' | local isFeasible = mod.feasible ~= 'No' | ||
if isFeasible then | if isFeasible then | ||
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']') | li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']') | ||
else | else | ||
local wrapper = li:tag('span') | local wrapper = li:tag('span') | ||
wrapper:addClass('mod-not-feasible') | wrapper:addClass('mod-not-feasible') | ||
| Line 887: | Line 1,002: | ||
addSectionRow(container, 'Documents') | addSectionRow(container, 'Documents') | ||
local timestamp = os.time() | local timestamp = os.time() | ||
math.randomseed(timestamp + pageId) | 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 901: | Line 1,015: | ||
) | ) | ||
local documentsCountWikitext = | local documentsCountWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
| Line 911: | 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 | ||
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 | ||
local docsQueryWikitext = | local docsQueryWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
| Line 931: | Line 1,042: | ||
local docsResult = frame:preprocess(docsQueryWikitext) | local docsResult = frame:preprocess(docsQueryWikitext) | ||
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') | ||
if docsResult then | if docsResult then | ||
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do | for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do | ||
local cells = {} | local cells = {} | ||
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | ||
local cleanContent = cellContent:gsub('<[^>]+>', '') | local cleanContent = cellContent:gsub('<[^>]+>', '') | ||
table.insert(cells, cleanContent) | table.insert(cells, cleanContent) | ||
end | end | ||
if #cells >= 2 then | if #cells >= 2 then | ||
local docType = trim(cells[2]) | local docType = trim(cells[2]) | ||
| Line 954: | Line 1,059: | ||
local docTitle = trim(cells[5]) | local docTitle = trim(cells[5]) | ||
local linkTarget = nil | local linkTarget = nil | ||
if docUrl then | if docUrl then | ||
linkTarget = docUrl | linkTarget = docUrl | ||
elseif docFile then | elseif docFile then | ||
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 | ||
local docPageName = trim(cells[1]) | local docPageName = trim(cells[1]) | ||
if docPageName then | if docPageName then | ||
local displayText = 'Document' | local displayText = 'Document' | ||
if docTitle then | if docTitle then | ||
| Line 976: | Line 1,077: | ||
end | end | ||
local li = ul:tag('li') | local li = ul:tag('li') | ||
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]') | li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]') | ||
| Line 986: | Line 1,086: | ||
docsDiv:node(ul) | docsDiv:node(ul) | ||
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 | uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]') | ||
docsDiv:node(uploadDiv) | docsDiv:node(uploadDiv) | ||
else | else | ||
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 | uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]') | ||
docsDiv:node(uploadDiv) | docsDiv:node(uploadDiv) | ||
end | end | ||
-- Links section | -- Links section | ||
local linksQueryWikitext = | local linksQueryWikitext = | ||
'{{#ask:\n' .. | '{{#ask:\n' .. | ||
| Line 1,014: | Line 1,112: | ||
local linksResult = frame:preprocess(linksQueryWikitext) | local linksResult = frame:preprocess(linksQueryWikitext) | ||
local linksByType = { | local linksByType = { | ||
Manufacturer = {}, | Manufacturer = {}, | ||
| Line 1,023: | Line 1,120: | ||
if linksResult then | if linksResult then | ||
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do | for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do | ||
local cells = {} | local cells = {} | ||
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do | ||
local cleanContent = cellContent:gsub('<[^>]+>', '') | local cleanContent = cellContent:gsub('<[^>]+>', '') | ||
table.insert(cells, trim(cleanContent)) | table.insert(cells, trim(cleanContent)) | ||
end | end | ||
if #cells >= 4 then | if #cells >= 4 then | ||
local linkType = cells[2] | local linkType = cells[2] | ||
| Line 1,049: | Line 1,142: | ||
end | end | ||
if hasLinks then | if hasLinks then | ||
addSectionRow(container, 'Archival Links') | addSectionRow(container, 'Archival Links') | ||
local linkTypeOrder = {'Database', 'Manufacturer', 'Retailer'} | local linkTypeOrder = {'Database', 'Manufacturer', 'Retailer'} | ||
for _, linkType in ipairs(linkTypeOrder) do | for _, linkType in ipairs(linkTypeOrder) do | ||
if #linksByType[linkType] > 0 then | if #linksByType[linkType] > 0 then | ||
local linksHtml = {} | local linksHtml = {} | ||
for _, link in ipairs(linksByType[linkType]) do | for _, link in ipairs(linksByType[linkType]) do | ||
| Line 1,070: | Line 1,160: | ||
addSectionRow(container, 'Contribute') | addSectionRow(container, 'Contribute') | ||
local editUrl = mw.uri.fullUrl( | local editUrl = mw.uri.fullUrl( | ||
"Special:FormStart", | "Special:FormStart", | ||
| Line 1,079: | Line 1,168: | ||
) | ) | ||
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;') | ||
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 styleTag .. tostring(container) | return styleTag .. tostring(container) | ||
end | end | ||
return p | return p | ||