Ambox notice.png
Scheduled Maintenance
The wiki will be going down for routine maintenance on Friday, October 4th, 2024, at approximately 3:30 PM Central Time (15:30) or 1:30 PM Pacific Time. The site may be inaccessible during this time and the database will be locked from editing. We expect the maintenance to take about thirty minutes. We strongly encourage joining our Discord for updates.

Module:Timeline: Difference between revisions

Jump to navigation Jump to search
en>BrandonXLF
(Added comments and added rowspan for labels)
 
m (1 revision imported)
 

Latest revision as of 19:55, 26 August 2021

This module implements the {{Timeline}} template. Please see the template page for usage instructions.


require('Module:No globals')

local yesno = require('Module:Yesno')
local navbox = require('Module:Navbox')._navbox
local getArgs = require('Module:Arguments').getArgs
local p = {}

-- Add a blank table cell
local function addBlank(args, row, prev, current)
	if row and prev < current then
		if yesno(args.decades) == false then
			row:tag('td')
				:addClass('timeline-blank')
				:cssText(args.blankstyle)
				:attr('colspan', current - prev)
		-- Divide the cell up every decade if showing decades at the top
		else
			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 entries, start years, and end years
local function timelineInfo(args)
	local info = {
		startYear = math.huge,
		startYears = {},
		endYear = 0,
		endYears = {},
		entries = {}
	}
	
	for k, _ in pairs(args) do
		if type(k) == 'string' then
			local num = k:match('^%a+(%d+)$')

			if num then
				table.insert(info.entries, tonumber(num))
			end
		end
	end
	
	table.sort(info.entries)
	
	for i, num in ipairs(info.entries) do 
		if args['item' .. i] then
			if not args['date' .. i] then
				error('item' .. i .. ' requires a corresponding ' .. 'date' .. i, 0)
			end
			
			local dates = mw.text.split(args['date' .. i], '-', true)
			local startYear = tonumber(dates[1])
			local endYear = tonumber(dates[2]) or tonumber(os.date('%Y')) + 1
			
			if not startYear then
				error('date' .. i .. ' contains an invalid timerange', 0)
			end
			
			info.startYear = math.min(info.startYear, startYear)
			info.endYear = math.max(info.endYear, endYear)
			info.startYears[i] = startYear
			info.endYears[i] = endYear
		end
	end
	
	if args.startoffset then
		info.startYear = info.startYear - tonumber(args.startoffset)
	end
	
	if args.startyear then
		info.startYear = math.min(info.startYear, tonumber(args.startyear))
	end
	
	if args.endoffset then
		info.endYear = info.endYear + tonumber(args.endoffset)
	end
	
	if args.endyear then
		info.endYear = math.max(info.endYear, tonumber(args.endyear))
	end
	
	return info
end

-- Render the date rows
local function renderDates(args, tbl, info)
	local showDecades = yesno(args.decades)
	local labelRow = nil
	
	if args.label then
		labelRow = mw.html.create('th')
			:attr('scope', 'col')
			:addClass('navbox-group timeline-label')
			:cssText(args.labelstyle)
			:attr('rowspan', showDecades ~= false and '2' or '1')
			:wikitext(args.label or '')
	end
	
	-- Render the decades row
	if showDecades ~= false then
		local decadeRow = tbl:tag('tr')
		local year = info.startYear
		
		decadeRow:node(labelRow)
		
		while year < info.endYear do
			local dur = math.min(10 - year % 10, info.endYear - 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
	end

	-- Render the years row
	local yearRow = tbl:tag('tr')
	local width = 100 / (info.endYear - info.startYear)
	
	if showDecades == false then
		yearRow:node(labelRow)
	end
	
	for i = info.startYear, info.endYear - 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

-- Render the timeline itself
local function renderTimeline(args, tbl, info)
	local row = nil
	local prev = info.startYear
	local prevItem = nil
	local prevLabel = nil
	local labelSpan = 0
	
	for i, num in ipairs(info.entries) do
		if args['row' .. i] or row == nil then
			addBlank(args, row, prev, info.endYear)
			
			row = tbl:tag('tr')
			prev = info.startYear
			
			if labelSpan <= 0 and args.label then
				labelSpan = tonumber(args['span' .. i]) or 1
				
				prevLabel = row:tag('th')
					:attr('scope', 'row')
					:attr('rowspan', labelSpan)
					:addClass('navbox-group timeline-label')
					:cssText(args.labelstyle)
					:cssText(args['labelstyle' .. i] or '')
					:wikitext(args['row' .. i])
			end
			
			labelSpan = labelSpan - 1
		end
		
		if args['item' .. i] then
			local content = args['item' .. i] 
			local startYear = info.startYears[i]
			local endYear = info.endYears[i]
			
			addBlank(args, row, prev, startYear)
			
			-- Shrink previous item so new item can start at the start year
			if prevItem and prev > startYear then
				prevItem:attr('colspan', prevItem:getAttr('colspan') - prev + startYear);
			end
			
			prevItem = row:tag('td')
				:addClass('timeline-item')
				:cssText(args.itemstyle)
				:cssText(args['style' .. i] or '')
				:attr('colspan', endYear - startYear)
				:wikitext(content)
			
			prev = endYear
		end
	end
	
	-- Remove any extra rowspan from the label
	if prevLabel and labelSpan > 0 then
		prevLabel:attr('rowspan', prevLabel:getAttr('rowspan') - labelSpan);
	end
	
	addBlank(args, row, prev, info.endYear)
end

function p.main(frame)
	local args = getArgs(frame, {
		removeBlanks = false,
		wrappers = 'Template:Timeline'
	})
	local targs = {
		listpadding = '0'
	}
	-- Arguments to passthrough to navbox
	local passthrough = {
		'name', 'title', 'above', 'below', 'state', 'navbar', 'border', 1,
		'image', 'imageleft', 'style', 'bodystyle', 'style', 'bodystyle',
		'basestyle', 'titlestyle', 'abovestyle', 'belowstyle', 'imagestyle',
		'imageleftstyle', 'titleclass', 'aboveclass', 'bodyclass',
		'belowclass', 'imageclass'
	}
	local info = timelineInfo(args)
	local tbl = mw.html.create('table'):addClass('timeline-table')
	
	renderDates(args, tbl, info)
	renderTimeline(args, tbl, info)
	
	if yesno(args.footer) then
		renderDates(args, tbl, info)
	end
	
	for _, name in ipairs(passthrough) do 
		targs[name] = args[name]
	end
	
	targs.list1 = frame:extensionTag{
		name = 'templatestyles',
		args = {
			src = 'Template:Timeline/style.css'
		}
	} .. tostring(tbl)
	
	return navbox(targs)
end

return p