Module:AgesEras
Documentation for this module may be created at Module:AgesEras/doc
local p = {}
local list = mw.loadData('Module:AgesEras/list')
local time = mw.language.getContentLanguage()
--note: if multiple ranges match the same age/era, the tag will duplicate, but will have the same name so won't be shown
--note: ages come before eras due to alphabetical sorting, but this could be changed in icon
--caution: pairs/ipairs and direct access are about the only things that will work on loadData tables
local function icon(kind, data, game, matchNum, gameNum)
if not data[1] or not data[2] or not data[3] or data[4] then
return '<span class="error">' ..
'invalid ' .. game .. ' ' .. kind .. ' data in [[Module:AgesEras/list]]' ..
'</span>'
end
local name = data[2]
local desc = data[3]
game = mw.ustring.upper(game)
local prefix
if kind == 'game' then
name = 'NPO'
prefix = '!' .. kind
kind = 'era'
else
prefix = kind
end
--[=[ Use this instead for console debugging:
return '<indicator name="' .. gameNum .. '-' .. mw.ustring.lower(game) .. '-' .. prefix .. '-' .. matchNum .. '-' .. mw.ustring.lower(name) .. '">' ..
'[' .. '[' .. 'File:' .. kind .. ' icon ' .. game .. ' ' .. name .. '.png|30px|' .. desc .. ']]' ..
'</indicator>'
]=]
return mw.getCurrentFrame():extensionTag(
'indicator',
'[' .. '[' .. 'File:' .. kind .. ' icon ' .. game .. ' ' .. name .. '.png|30px|' .. desc .. ']]',
{ name = gameNum .. '-' .. mw.ustring.lower(game) .. '-' .. prefix .. '-' .. matchNum .. '-' .. mw.ustring.lower(name) }
)
end
--Returns { { start, end }, ... }
local function argToTable(arg)
if not arg or mw.text.trim(arg) == '' then return {} end
local parsed = {}
arg = mw.ustring.lower(arg)
local rangeSep = ','
if mw.ustring.find(arg, ';', 1, true) then
rangeSep = ';'
elseif not mw.ustring.find(arg, '-', 1, true) and #mw.text.split(arg, ',', true) == 2 then
--This looks like a single date, don't want to split on comma
rangeSep = '&'
end
dateSep = ' - '
if mw.ustring.find(arg, ' to ', 1, true) then
dateSep = ' to '
end
mw.ustring.gsub(arg, ' until ', dateSep)
local ranges = mw.text.split(arg, rangeSep, true)
local dates
for i, range in ipairs(ranges) do
dates = mw.text.split(mw.text.trim(range), dateSep, true)
if #dates == 1 then
table.insert(dates, 'present')
elseif #dates > 2 then
dates = { 'invalid date range: ' .. range }
else
dates[1] = mw.text.trim(dates[1])
dates[2] = mw.text.trim(dates[2])
if dates[1] == '' then dates[1] = 'unknown' end
if dates[2] == '' then dates[2] = 'unknown' end
if dates[1] == 'present' then dates[1] = 'unknown' end
if dates[1] == 'unknown' then
dates[1] = dates[2]
end
if dates[2] == 'unknown' then
if dates[1] == 'unknown' then
dates = { 'invalid date range: ' .. range }
else
dates[2] = dates[1]
end
end
end
table.insert(parsed, dates)
end
return parsed
end
local function findMatches(itemList, dates)
if not itemList or not itemList[1] then return {} end
local matches = {}
local start = dates[1]
--Note: start can be present if it was unknown and finish is present
if start == 'present' then start = '' end
local finish = dates[2]
if finish == 'present' then finish = '' end
--formatDate works same as #time, U is Unix time
start = time:formatDate('U', start)
finish = time:formatDate('U', finish)
local itemStart, itemFinish, nextItem
for i, item in ipairs(itemList) do
itemStart = time:formatDate('U', item[1])
nextItem = itemList[i + 1]
if nextItem then
nextItem = nextItem[1]
else
nextItem = ''
end
itemFinish = time:formatDate('U', nextItem)
--Note: this won't count being present for only the first or last day
if (start <= itemStart and finish > itemStart) or (start > itemStart and start < itemFinish) then
table.insert(matches, item)
end
end
return matches
end
local function generateCode(dates, game, num)
if #dates == 0 then return '' end
local code = ''
local gameList = list[game]
if not gameList then return '' end
for i, range in ipairs(dates) do
if #range == 1 then
code = code .. '<span class="error">' .. range[1] .. '</span>'
else
if i == 1 and gameList.game then
code = code .. icon('game', gameList.game, game, i, num)
end
for j, ageMatch in ipairs(findMatches(gameList.ages, range)) do
code = code .. icon('age', ageMatch, game, j, num)
end
for k, eraMatch in ipairs(findMatches(gameList.eras, range)) do
code = code .. icon('era', eraMatch, game, k, num)
end
end
end
return code
end
function p.main(frame)
return p.call(frame:getParent().args)
end
function p.call(args)
local code = ''
--Should be in order of branch establishment, this will determine display order.
local games = {
argToTable(args.ns),
argToTable(args.cn),
}
--This is a map to /list keys, must be in same order as above.
local gameCodes = { 'ns', 'cn', }
for i, gameTable in ipairs(games) do
code = code .. generateCode(gameTable, gameCodes[i], i)
end
return code
end
return p