Jump to content

Module:Navbox timeline/sandbox

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by BrandonXLF (talk | contribs) at 03:58, 9 June 2022 (Use new format without numbering 🥳). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
require('Module:No globals')

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
			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 row data (cells, label, etc.), start year, and end year
local function parseRow(text)
	if text:sub(-1) ~= '\n' then
		text = text .. '\n'
	end
	
	local minYear = math.huge
	local maxYear = 0
	local rowData = {
		cells = {}
	}
	
	for line in s:gmatch('(.-)\n') do
		local key, val = line:match('[^: ]: *(.*)')
		
		-- Key value pairs
		if #cells == 0 and key and val then
			if #cells == 0 then
				rowData[key] = val
			else
				rowData.cells[#cells][key] = val
			end
			
		-- Date (cell with item already)
		elseif cell[#cells - 1].item then
			local dates = mw.text.split(line, '-', true)
			local startYear = tonumber(dates[1])
			local endYear = tonumber(dates[2]) or tonumber(os.date('%Y')) + 1
			
			if not startYear then
				error('date' ..  suffix .. ' contains an invalid timerange', 0)
			end

			rowData.cells[#cells].startYear = startYear
			rowData.cells[#cells].endYear = endYear
			
			if startYear < minYear then
				minYear = startYear
			end

			if endYear > maxYear then
				maxYear = endYear
			end
			
		-- Item
		else
			table.insert(rowData.cells, {
				item = item
			})
		end
	end
	
	return rowData, minYear, maxYear
end

-- Get timeline rows, start year, and end year
local function timelineInfo(args)
	local minYear = math.huge
	local maxYear = 0
	local rows = {}
	
	for k, v in ipairs(args) do
		local rowData, rowMinYear, rowMaxYear =  parseRow(v)

		if rowMinYear < minYear then
			minYear = rowMinYear
		end

		if rowMaxYear > maxYear then
			maxYear = rowMaxYear
		end

		table.insert(info.rows, rowData)
	end
	
	if args.startoffset then
		minYear = minYear - tonumber(args.startoffset)
	end
	
	if args.startyear and tonumber(args.startyear) < minYear then
		minYear = tonumber(args.startyear)
	end
	
	if args.endoffset then
		maxYear = maxYear + tonumber(args.endoffset)
	end
	
	if args.endyear and tonumber(args.endyear) > maxYear then
		maxYear = tonumber(args.endyear)
	end
	
	return rows, minYear, maxYear
end

-- Render the date rows
local function renderDates(args, tbl, startYear, endYear)
	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')
			:addClass('timeline-row')
		local year = startYear
		
		decadeRow:node(labelRow)
		
		while year < endYear do
			local dur = math.min(10 - year % 10, 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')
		:addClass('timeline-row')
	local width = 100 / (endYear - startYear)
	
	if showDecades == false then
		yearRow:node(labelRow)
	end
	
	for i = startYear, 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, rows, minYear, maxYear)
	local rowElement = nil
	local rowSuffix = nil
	local prev = minYear
	local prevItem = nil
	local prevLabel = nil
	local labelSpan = 0
	
	for _, row in ipairs(rows) do
		local rowElement = tbl:tag('tr')
			:addClass('timeline-row')
		
		if labelSpan <= 0 and args.label then
			labelSpan = tonumber(args['span' .. rowSuffix]) or 1
			
			prevLabel = rowElement:tag('th')
				:attr('scope', 'row')
				:attr('rowspan', labelSpan)
				:addClass('navbox-group timeline-label')
				:cssText(args.labelstyle)
				:cssText(args['headstyle' .. rowSuffix] or '')
				:wikitext(args['head' .. rowSuffix])
		end
		
		labelSpan = labelSpan - 1
		
		local content = args['item' ..  suffix] 
		local prevEndYear = minYear
		local prevItem = nil
		
		for _, cell in ipairs(row.cells) do
			-- 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
			
			-- Add blanks before the cell
			addBlank(args, rowElement, prevEndYear, cell.startYear)
			
			prevItem = rowElement:tag('td')
				:addClass('timeline-item')
				:cssText(args.itemstyle)
				:cssText(args['style' ..  suffix] or '')
				:attr('colspan', endYear - startYear)
				:wikitext(content)

			prevEndYear = cell.endYear
		end
		
		-- Add blanks to the end of the row
		addBlank(args, rowElement, prevEndYear, maxYear)
	end
	
	-- Remove any extra rowspan from the label
	if prevLabel and labelSpan > 0 then
		prevLabel:attr('rowspan', prevLabel:getAttr('rowspan') - labelSpan);
	end
	
	-- Add blanks to thr end of the last row
	-- addBlank(args, rowElement, 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 rows, minYear, maxYear = timelineInfo(args)
	local wrapper = mw.html.create('table')
		:addClass('timeline-wrapper')
	local tbl = wrapper:tag('tr')
		:tag('td')
			:addClass('timeline-wrapper-cell')
			:tag('table')
				:addClass('timeline-table')
	
	renderDates(args, tbl, minYear, maxYear)
	renderTimeline(args, tbl, rows, minYear, maxYear)
	
	if yesno(args.footer) then
		renderDates(args, tbl, minYear, maxYear)
	end
	
	for _, name in ipairs(passthrough) do 
		targs[name] = args[name]
	end

	targs.list1 = frame:extensionTag{
		name = 'templatestyles',
		args = {
			src = 'Module:Timeline/styles.css'
		}
	} .. tostring(wrapper)
	
	return navbox(targs)
end

return p