Jump to content

Module:Navbox timeline: Difference between revisions

From Rolling Lounge Wiki
From Wikipedia (https://en.wikipedia.org/wiki/Module:Navbox_timeline)
 
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
:cssText(args.blankstyle)
local date = prev
:attr('colspan', current - 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', showDecades ~= false and '2' or '1')
: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 i = rows.minYear, rows.maxYear - 1 do
for year = rows.minYear, rows.maxYear - 1 do
yearRow:tag('th')
yearRow:tag('th')
:attr('scope', 'col')
:attr('scope', 'col')
:addClass('timeline-year')
:addClass('timeline-year')
:cssText(args.datestyle)
:cssText(args.datestyle)
:cssText(args.yearstyle)
:cssText(args.yearstyle)
:cssText('width:' .. width .. '%')
:attr('colspan', '12')
:wikitext(showDecades == false and i or i % 10)
: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:getAttr('colspan') - prev + prevEndYear);
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', cell.endYear - cell.startYear)
: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). Wikipedia logo

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