Module:Navbox timeline: Difference between revisions
Appearance
AZMindroma (talk | contribs) From Wikipedia (https://en.wikipedia.org/wiki/Module:Navbox_timeline) |
AZMindroma (talk | contribs) Trying out months change (AI) Tag: Reverted |
||
| Line 10: | Line 10: | ||
if row and prev < current then | if row and prev < current then | ||
if yesno(args.decades) == false then | if yesno(args.decades) == false then | ||
row:tag('td') | if yesno(args.months) ~= false then | ||
:addClass('timeline-blank') | -- Divide the cell up by months when showing months | ||
local date = prev | |||
while date < current do | |||
local dur = math.min(1, current - date) -- 1 month duration | |||
row:tag('td') | |||
:addClass('timeline-blank') | |||
:cssText(args.blankstyle) | |||
:attr('colspan', dur) | |||
date = date + dur | |||
end | |||
else | |||
row:tag('td') | |||
:addClass('timeline-blank') | |||
:cssText(args.blankstyle) | |||
:attr('colspan', current - prev) | |||
end | |||
else | else | ||
-- Divide the cell up every decade if showing decades at the top | -- Divide the cell up every decade if showing decades at the top | ||
| Line 113: | Line 129: | ||
return rows | return rows | ||
end | |||
-- Get month abbreviation | |||
local function getMonthAbbr(month) | |||
local months = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', | |||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'} | |||
return months[month] or '' | |||
end | end | ||
| Line 118: | Line 141: | ||
local function renderDates(args, tbl, rows, invert) | local function renderDates(args, tbl, rows, invert) | ||
local showDecades = yesno(args.decades) | local showDecades = yesno(args.decades) | ||
local showMonths = yesno(args.months) | |||
local yearRow = tbl:tag('tr') | local yearRow = tbl:tag('tr') | ||
:addClass('timeline-row') | :addClass('timeline-row') | ||
| Line 123: | Line 147: | ||
-- Create label | -- Create label | ||
if args.label or rows.hasLabels then | if args.label or rows.hasLabels then | ||
local rowspan = 1 | |||
if showDecades ~= false then | |||
rowspan = 2 | |||
elseif showDecades == false and showMonths ~= false then | |||
rowspan = 2 | |||
end | |||
local labelCell = mw.html.create('th') | local labelCell = mw.html.create('th') | ||
:attr('scope', 'col') | :attr('scope', 'col') | ||
:addClass('navbox-group timeline-label') | :addClass('navbox-group timeline-label') | ||
:cssText(args.labelstyle) | :cssText(args.labelstyle) | ||
:attr('rowspan', | :attr('rowspan', tostring(rowspan)) | ||
:wikitext(args.label or '') | :wikitext(args.label or '') | ||
| Line 156: | Line 187: | ||
year = year + dur | year = year + dur | ||
end | |||
-- Create month row when decades are disabled but months are enabled | |||
elseif showDecades == false and showMonths ~= false then | |||
local monthRow = tbl:tag('tr') | |||
:addClass('timeline-row') | |||
-- Move month row | |||
if not invert then | |||
monthRow, yearRow = yearRow, monthRow | |||
end | |||
-- Calculate total months in range | |||
local totalMonths = (rows.maxYear - rows.minYear) * 12 | |||
local monthWidth = 100 / totalMonths | |||
for year = rows.minYear, rows.maxYear - 1 do | |||
for month = 1, 12 do | |||
monthRow:tag('th') | |||
:attr('scope', 'col') | |||
:addClass('timeline-month') | |||
:cssText(args.datestyle) | |||
:cssText(args.monthstyle) | |||
:cssText('width:' .. monthWidth .. '%') | |||
:wikitext(getMonthAbbr(month)) | |||
end | |||
end | end | ||
end | end | ||
-- Populate year row element | -- Populate year row element | ||
local width = 100 / (rows.maxYear - rows.minYear) | if showDecades == false and showMonths ~= false then | ||
-- When showing months, each year spans 12 columns | |||
for year = rows.minYear, rows.maxYear - 1 do | |||
yearRow:tag('th') | |||
:attr('scope', 'col') | |||
:addClass('timeline-year') | |||
:cssText(args.datestyle) | |||
:cssText(args.yearstyle) | |||
:attr('colspan', '12') | |||
:wikitext(tostring(year)) | |||
end | |||
else | |||
-- Normal year display | |||
local width = 100 / (rows.maxYear - rows.minYear) | |||
for i = rows.minYear, rows.maxYear - 1 do | |||
yearRow:tag('th') | |||
:attr('scope', 'col') | |||
:addClass('timeline-year') | |||
:cssText(args.datestyle) | |||
:cssText(args.yearstyle) | |||
:cssText('width:' .. width .. '%') | |||
:wikitext(showDecades == false and i or i % 10) | |||
end | |||
end | end | ||
end | end | ||
| Line 206: | Line 276: | ||
-- Shrink previous item so new item can start at the start year | -- Shrink previous item so new item can start at the start year | ||
if prevItem and prev > prevEndYear then | if prevItem and prev > prevEndYear then | ||
prevItem:attr('colspan', prevItem: | local colspan = prevItem:getAttr('colspan') | ||
if yesno(args.decades) == false and yesno(args.months) ~= false then | |||
-- Adjust for months | |||
prevItem:attr('colspan', colspan - (prev - prevEndYear) * 12) | |||
else | |||
prevItem:attr('colspan', colspan - prev + prevEndYear) | |||
end | |||
end | end | ||
| Line 215: | Line 291: | ||
-- Add blanks before the cell | -- Add blanks before the cell | ||
addBlank(args, rowElement, prevEndYear, cell.startYear) | addBlank(args, rowElement, prevEndYear, cell.startYear) | ||
local colspan = cell.endYear - cell.startYear | |||
if yesno(args.decades) == false and yesno(args.months) ~= false then | |||
-- Each year represents 12 months | |||
colspan = colspan * 12 | |||
end | |||
prevItem = rowElement:tag('td') | prevItem = rowElement:tag('td') | ||
| Line 220: | Line 302: | ||
:cssText(args.itemstyle) | :cssText(args.itemstyle) | ||
:cssText(cell.style or '') | :cssText(cell.style or '') | ||
:attr('colspan', | :attr('colspan', colspan) | ||
:wikitext(cell.item) | :wikitext(cell.item) | ||
Revision as of 09:12, 5 June 2025
| This article uses material from the Wikipedia article Module:Navbox timeline, which is released under the Creative Commons Attribution-ShareAlike 4.0 International License (view authors). |
require('strict')
local yesno = require('Module:Yesno')
local navbox = require('Module:Navbox')._navbox
local getArgs = require('Module:Arguments').getArgs
local p = {}
-- Add blank table cells
local function addBlank(args, row, prev, current)
if row and prev < current then
if yesno(args.decades) == false then
if yesno(args.months) ~= false then
-- Divide the cell up by months when showing months
local date = prev
while date < current do
local dur = math.min(1, current - date) -- 1 month duration
row:tag('td')
:addClass('timeline-blank')
:cssText(args.blankstyle)
:attr('colspan', dur)
date = date + dur
end
else
row:tag('td')
:addClass('timeline-blank')
:cssText(args.blankstyle)
:attr('colspan', current - prev)
end
else
-- Divide the cell up every decade if showing decades at the top
local year = prev
while year < current do
local dur = math.min(10 - year % 10, current - year)
row:tag('td')
:addClass('timeline-blank')
:cssText(args.blankstyle)
:attr('colspan', dur)
year = year + dur
end
end
end
end
-- Get timeline data
local function timelineInfo(args)
local rows = {
[1] = {},
minYear = math.huge,
maxYear = -math.huge,
hasLabels = false
}
for num, fullVal in ipairs(args) do
local key, val = fullVal:match('^([a-z]+): *(.*)$')
local cellIndex = #rows[#rows]
-- Row data key value pairs
if cellIndex == 0 and key then
rows[#rows][key] = val
-- Record that there are labels
if key == 'label' then
rows.hasLabels = true
end
-- Data cell key value pairs
elseif key then
-- Data cell key value pairs
rows[#rows][cellIndex][key] = val
-- New row
elseif fullVal == '' then
if next(rows[#rows]) then
table.insert(rows, {})
end
-- Add date to cell with item already and no date
elseif cellIndex > 0
and rows[#rows][cellIndex].item
and not rows[#rows][cellIndex].startYear
then
local dates = mw.text.split(fullVal, '-', true)
local startYear = tonumber(dates[1])
local endYear = tonumber(dates[2]) or tonumber(os.date('%Y')) + 1
if startYear == nil then
error('Argument ' .. num .. ' is an invalid time range', 0)
end
if endYear < startYear then
error('Argument ' .. num .. '\'s end year is less than the start year', 0)
end
rows[#rows][cellIndex].startYear = startYear
rows[#rows][cellIndex].endYear = endYear
if startYear < rows.minYear then
rows.minYear = startYear
end
if endYear > rows.maxYear then
rows.maxYear = endYear
end
-- New item
else
table.insert(rows[#rows], { item = fullVal })
end
end
-- Add overrides from arguments
if args.startoffset then
rows.minYear = rows.minYear - tonumber(args.startoffset)
end
if args.startyear and tonumber(args.startyear) < rows.minYear then
rows.minYear = tonumber(args.startyear)
end
if args.endoffset then
rows.maxYear = rows.maxYear + tonumber(args.endoffset)
end
if args.endyear and tonumber(args.endyear) > rows.maxYear then
rows.maxYear = tonumber(args.endyear)
end
return rows
end
-- Get month abbreviation
local function getMonthAbbr(month)
local months = {'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'}
return months[month] or ''
end
-- Render the date rows
local function renderDates(args, tbl, rows, invert)
local showDecades = yesno(args.decades)
local showMonths = yesno(args.months)
local yearRow = tbl:tag('tr')
:addClass('timeline-row')
-- Create label
if args.label or rows.hasLabels then
local rowspan = 1
if showDecades ~= false then
rowspan = 2
elseif showDecades == false and showMonths ~= false then
rowspan = 2
end
local labelCell = mw.html.create('th')
:attr('scope', 'col')
:addClass('navbox-group timeline-label')
:cssText(args.labelstyle)
:attr('rowspan', tostring(rowspan))
:wikitext(args.label or '')
yearRow:node(labelCell)
end
-- Create decade row
if showDecades ~= false then
local decadeRow = tbl:tag('tr')
:addClass('timeline-row')
local year = rows.minYear
-- Move decade row
if not invert then
decadeRow, yearRow = yearRow, decadeRow
end
while year < rows.maxYear do
local dur = math.min(10 - year % 10, rows.maxYear - year)
decadeRow:tag('th')
:attr('scope', 'col')
:addClass('timeline-decade')
:cssText(args.datestyle)
:cssText(args.decadestyle)
:attr('colspan', dur)
:wikitext(math.floor(year / 10) .. '0s')
year = year + dur
end
-- Create month row when decades are disabled but months are enabled
elseif showDecades == false and showMonths ~= false then
local monthRow = tbl:tag('tr')
:addClass('timeline-row')
-- Move month row
if not invert then
monthRow, yearRow = yearRow, monthRow
end
-- Calculate total months in range
local totalMonths = (rows.maxYear - rows.minYear) * 12
local monthWidth = 100 / totalMonths
for year = rows.minYear, rows.maxYear - 1 do
for month = 1, 12 do
monthRow:tag('th')
:attr('scope', 'col')
:addClass('timeline-month')
:cssText(args.datestyle)
:cssText(args.monthstyle)
:cssText('width:' .. monthWidth .. '%')
:wikitext(getMonthAbbr(month))
end
end
end
-- Populate year row element
if showDecades == false and showMonths ~= false then
-- When showing months, each year spans 12 columns
for year = rows.minYear, rows.maxYear - 1 do
yearRow:tag('th')
:attr('scope', 'col')
:addClass('timeline-year')
:cssText(args.datestyle)
:cssText(args.yearstyle)
:attr('colspan', '12')
:wikitext(tostring(year))
end
else
-- Normal year display
local width = 100 / (rows.maxYear - rows.minYear)
for i = rows.minYear, rows.maxYear - 1 do
yearRow:tag('th')
:attr('scope', 'col')
:addClass('timeline-year')
:cssText(args.datestyle)
:cssText(args.yearstyle)
:cssText('width:' .. width .. '%')
:wikitext(showDecades == false and i or i % 10)
end
end
end
-- Render the timeline itself
local function renderTimeline(args, tbl, rows)
local rowElement = nil
local rowSuffix = nil
local prev = rows.minYear
local prevItem = nil
local prevLabel = nil
local labelSpan = 0
for rowNum, row in ipairs(rows) do
local rowElement = tbl:tag('tr')
:addClass('timeline-row')
if labelSpan <= 0 and (rows.hasLabels or args.label) then
labelSpan = tonumber(row.span) or 1
prevLabel = rowElement:tag('th')
:attr('scope', 'row')
:attr('rowspan', labelSpan)
:addClass('navbox-group timeline-label')
:cssText(args.labelstyle)
:cssText(row.labelstyle)
:wikitext(row.label)
end
labelSpan = labelSpan - 1
local prevEndYear = rows.minYear
local prevItem = nil
for cellNum, cell in ipairs(row) do
-- Shrink previous item so new item can start at the start year
if prevItem and prev > prevEndYear then
local colspan = prevItem:getAttr('colspan')
if yesno(args.decades) == false and yesno(args.months) ~= false then
-- Adjust for months
prevItem:attr('colspan', colspan - (prev - prevEndYear) * 12)
else
prevItem:attr('colspan', colspan - prev + prevEndYear)
end
end
if cell.startYear == nil then
error('Missing timerange for row ' .. rowNum .. ' cell ' .. cellNum, 0)
end
-- Add blanks before the cell
addBlank(args, rowElement, prevEndYear, cell.startYear)
local colspan = cell.endYear - cell.startYear
if yesno(args.decades) == false and yesno(args.months) ~= false then
-- Each year represents 12 months
colspan = colspan * 12
end
prevItem = rowElement:tag('td')
:addClass('timeline-item')
:cssText(args.itemstyle)
:cssText(cell.style or '')
:attr('colspan', colspan)
:wikitext(cell.item)
prevEndYear = cell.endYear
end
-- Add blanks to the end of the row
addBlank(args, rowElement, prevEndYear, rows.maxYear)
end
-- Remove any extra rowspan from the label
if prevLabel and labelSpan > 0 then
prevLabel:attr('rowspan', prevLabel:getAttr('rowspan') - labelSpan);
end
end
function p.main(frame)
local args = getArgs(frame, {
removeBlanks = false,
wrappers = 'Template:Navbox timeline'
})
local targs = {
listpadding = '0'
}
-- Arguments to passthrough to navbox
local passthrough = {
'name', 'title', 'above', 'below', 'state', 'navbar', 'border', 'image',
'imageleft', 'style', 'bodystyle', 'style', 'bodystyle', 'basestyle',
'titlestyle', 'abovestyle', 'belowstyle', 'imagestyle',
'imageleftstyle', 'titleclass', 'aboveclass', 'bodyclass', 'belowclass',
'imageclass'
}
local rows = timelineInfo(args)
local wrapper = mw.html.create('table')
:addClass('timeline-wrapper notheme')
local tbl = wrapper:tag('tr')
:tag('td')
:addClass('timeline-wrapper-cell')
:tag('table')
:addClass('timeline-table')
renderDates(args, tbl, rows)
renderTimeline(args, tbl, rows)
if yesno(args.footer) then
renderDates(args, tbl, rows, true)
end
for _, name in ipairs(passthrough) do
targs[name] = args[name]
end
targs.templatestyles = 'Module:Navbox timeline/styles.css'
targs.list1 = tostring(wrapper)
return navbox(targs)
end
return p