# Module:Timeline

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
local args = getArgs(, {
removeBlanks = false,
wrappers = 'Template:Timeline'
})
local targs = {
listpadding = '0'
}
-- Arguments to passthrough to navbox
local passthrough = {
'name', 'title', 'above', 'below', 'state', 'navbar', '', 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 = :extensionTag{
name = 'templatestyles',
args = {
src = 'Template:Timeline/style.css'
}
} .. tostring(tbl)  
return navbox(targs)
end  
return p