Module:Timeline
From Halopedia, the Halo wiki
Documentation for this module may be created at ModuleDoc:Timeline
local p = {}
-- Cache for page existence checks
local existenceCache = {}
-- This checks if a wiki page for a given year exists.
local function pageExists(year)
local key = tostring(year)
if existenceCache[key] ~= nil then
return existenceCache[key]
end
local title = mw.title.new(key)
local exists = title and title.exists
existenceCache[key] = exists
return exists
end
function p.main(frame)
local args = frame:getParent().args
local title = mw.title.getCurrentTitle().text
-- Detect year and era (Example: 2552, 480 BCE, so on)
local yearStr = args.year or title
local yearNum, era
-- This checks if the year is BCE or CE
if yearStr:match("%d+%s*[BCE]+$") then
yearNum = tonumber(yearStr:match("%d+"))
era = "BCE"
else
yearNum = tonumber(yearStr:match("%d+")) or 0
era = "CE"
end
-- This stops 0 from being used. (Kinda a null thing, but hey)
if yearNum == 0 then
return '<!-- Invalid year -->'
end
-- Parse ignore list (allows us to hide specific years from the smart nav and decade grid)
local ignoreSet = {}
if args.ignore and args.ignore ~= '' then
for y in mw.text.gsplit(args.ignore, ",", true) do
y = mw.text.trim(y)
local num = tonumber(y:match("%d+"))
if num then ignoreSet[num] = true end
end
end
-- Use this to style the template.
local html = mw.html.create('div')
:addClass('infobox')
:css({
float = 'right',
width = '300px',
margin = '0 0 1em 1em',
padding = '8px',
border = '1px solid #aaa',
background = '#f9f9f9',
['border-radius'] = '4px'
})
-- Timeline Logo Header (Update this if a new image is created)
local logoClass = ''
if args.image and args.image ~= '' then
logoClass = 'notpageimage'
end
html:tag('div')
:css('text-align', 'center')
:css('margin-bottom', '10px')
:wikitext('[[File:HP-Timeline.png|300px|link=Timeline|alt=Timeline|class=' .. logoClass .. ']]')
-- Year Header
local displayYear = (era == "BCE") and (yearNum .. " BCE") or tostring(yearNum)
html:tag('div')
:addClass('infobox-header')
:css('text-align', 'center')
:css('font-weight', 'bold')
:css('font-size', '200%')
:wikitext(displayYear)
-- Year image
if args.image and args.image ~= '' then
html:tag('div')
:css('text-align', 'center')
:css('margin', '10px 0 6px 0')
:wikitext('[[' .. args.image .. '|300px]]')
-- Image caption
if args.caption and args.caption ~= '' then
html:tag('div')
:css('text-align', 'center')
:css('font-size', 'larger')
:css('margin-top', '4px')
:wikitext(args.caption)
end
end
-- Other calendars
if args.other and args.other ~= '' then
html:tag('div')
:css('margin-top', '10px')
:css('text-align', 'center')
:wikitext(args.other)
end
local function isUsable(y)
if not y or ignoreSet[y] then return false end
if y < 1 and era == "CE" then return false end
local page = (era == "BCE") and (y .. " BCE") or tostring(y)
return pageExists(page)
end
-- Override code - CIA note: This is needed for years so stuff doesnt break with timespans that go beyond the capabilities of Mediawiki.
local manual = {
previous1 = args.previous1 or args.manualprevious1,
previous2 = args.previous2 or args.manualprevious2,
next1 = args.next1 or args.manualnext1,
next2 = args.next2 or args.manualnext2
}
-- Optimized find functions (with cache + limit)
local function findNearestPrev(year, currentEra)
local maxSearch = 500
if currentEra == "BCE" then
local y = year + 1
for _ = 1, maxSearch do
if y > 3000 then break end
if not ignoreSet[y] and pageExists(y .. " BCE") then return y, "BCE" end
y = y + 1
end
else
local y = year - 1
for _ = 1, maxSearch do
if y < 1 then break end
if not ignoreSet[y] and pageExists(tostring(y)) then return y, "CE" end
y = y - 1
end
local y = 3000
for _ = 1, maxSearch do
if y < 1 then break end
if not ignoreSet[y] and pageExists(y .. " BCE") then return y, "BCE" end
y = y - 1
end
end
return nil
end
local function findNearestNext(year, currentEra)
local maxSearch = 500
if currentEra == "BCE" then
local y = year - 1
for _ = 1, maxSearch do
if y < 1 then break end
if not ignoreSet[y] and pageExists(y .. " BCE") then return y, "BCE" end
y = y - 1
end
local y = 1
for _ = 1, maxSearch do
if y > 3000 then break end
if not ignoreSet[y] and pageExists(tostring(y)) then return y, "CE" end
y = y + 1
end
else
local y = year + 1
for _ = 1, maxSearch do
if y > 3000 then break end
if not ignoreSet[y] and pageExists(tostring(y)) then return y, "CE" end
y = y + 1
end
end
return nil
end
local function makeLink(y, text, isBold, targetEra)
if not y then
return '<span style="color:#888">' .. (text or '?') .. '</span>'
end
local page = (targetEra == "BCE") and (y .. " BCE") or tostring(y)
local display = (targetEra == "BCE") and (y .. " BCE") or tostring(y)
if text then display = text end
local link = '[[' .. page .. '|' .. display .. ']]'
return isBold and "'''" .. link .. "'''" or link
end
-- === SMART NAVIGATION (THIS SECTION SUCKS OH MY GOONDESS) ===
local nav = html:tag('div')
:css('margin-top', '12px')
:css('text-align', 'center')
:css('font-size', 'larger')
if era == "BCE" then
local farPrev = manual.previous1 or findNearestPrev( findNearestPrev(yearNum, "BCE") or yearNum, "BCE" )
local nearPrev = manual.previous2 or findNearestPrev(yearNum, "BCE")
local nearNext = manual.next1 or findNearestNext(yearNum, "BCE")
local farNext = manual.next2 or findNearestNext(nearNext or yearNum, nearNext and "BCE" or "CE")
local parts = {
farPrev and makeLink(farPrev, nil, false, "BCE") or "«",
nearPrev and makeLink(nearPrev, nil, false, "BCE") or "",
makeLink(yearNum, nil, true, "BCE"),
nearNext and makeLink(nearNext, nil, false, "BCE") or "",
farNext and makeLink(farNext, nil, false, "CE") or "»"
}
nav:wikitext(table.concat(parts, " • "))
else
local farPrev = manual.previous1 or findNearestPrev( findNearestPrev(yearNum, "CE") or yearNum, "CE" )
local nearPrev = manual.previous2 or findNearestPrev(yearNum, "CE")
local nearNext = manual.next1 or findNearestNext(yearNum, "CE")
local farNext = manual.next2 or findNearestNext(nearNext or yearNum, "CE")
local parts = {
farPrev and makeLink(farPrev, nil, false, "CE") or "«",
nearPrev and makeLink(nearPrev, nil, false, "CE") or "",
makeLink(yearNum, nil, true, "CE"),
nearNext and makeLink(nearNext, nil, false, "CE") or "",
farNext and makeLink(farNext, nil, false, "CE") or "»"
}
nav:wikitext(table.concat(parts, " • "))
end
-- Decade Grid with 0s exception
local decadeStart = math.floor(yearNum / 10) * 10
local decadeLabel = tostring(decadeStart) .. "s"
if era == "BCE" then decadeLabel = decadeLabel .. " BCE" end
local decadeDiv = html:tag('div')
:css('margin-top', '14px')
:css('text-align', 'center')
:css('font-size', 'larger')
:css('line-height', '1.8')
decadeDiv:wikitext("'''Years in the " .. decadeLabel .. "'''<br>")
local firstRowStart = (decadeStart == 0 and era == "CE") and 1 or 0
for i = firstRowStart, 4 do
local y = decadeStart + i
local text = tostring(y) .. (era == "BCE" and " BCE" or "")
local link = isUsable(y) and makeLink(y, text, y == yearNum, era) or '<span style="color:#888">' .. text .. '</span>'
decadeDiv:wikitext(link)
if i < 4 then decadeDiv:wikitext(' • ') end
end
decadeDiv:wikitext('<br>')
for i = 5, 9 do
local y = decadeStart + i
local text = tostring(y) .. (era == "BCE" and " BCE" or "")
local link = isUsable(y) and makeLink(y, text, y == yearNum, era) or '<span style="color:#888">' .. text .. '</span>'
decadeDiv:wikitext(link)
if i < 9 then decadeDiv:wikitext(' • ') end
end
-- Footer
html:tag('div')
:css('margin-top', '12px')
:css('text-align', 'center')
:css('font-size', '85%')
:wikitext("For a complete list, see the [[:Category:Timeline|timeline category]].")
return html
end
return p