Module:AgesEras: Difference between revisions
Jump to navigation
Jump to search
(various fixes and improvements) |
m (use a non-special character to sort game icon) |
||
(5 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
local list = mw.loadData('Module:AgesEras/list') | local list = mw.loadData('Module:AgesEras/list') | ||
local | local lang = mw.language.getContentLanguage() | ||
local testMode = false | local testMode = false | ||
--arg names must match gameorder keys in /list module | --arg names must match gameorder keys in /list module | ||
--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: if multiple ranges match the same age/era, the tag will duplicate, but will have the same name so won't be shown | ||
Line 10: | Line 10: | ||
local function icon(kind, data, game, gameNum) | local function icon(kind, data, game, gameNum) | ||
local frame = mw.getCurrentFrame() | |||
--Pad to two digits | --Pad to two digits | ||
gameNum = mw.ustring.format('%02d', gameNum) | gameNum = mw.ustring.format('%02d', gameNum) | ||
Line 20: | Line 21: | ||
end | end | ||
game = mw.ustring.upper(game) | game = mw.ustring.upper(game) | ||
local gameFull = | local gameFull = frame:expandTemplate({ title='abbrgame', args={ game, 'full' } }) | ||
local name = item[2] | local name = item[2] | ||
local desc = item[3] | local desc = item[3] | ||
Line 26: | Line 27: | ||
if kind == 'game' then | if kind == 'game' then | ||
name = 'NPO' | name = 'NPO' | ||
prefix = ' | prefix = 'a' .. kind | ||
kind = 'era' | kind = 'era' | ||
link = 'New Pacific Order (' .. gameFull .. ')' | link = 'New Pacific Order (' .. gameFull .. ')' | ||
else | else | ||
prefix = kind | prefix = kind | ||
link = | link = 'History of the New Pacific Order in ' .. gameFull .. '#' .. desc | ||
end | end | ||
local indicatorName = gameNum .. '-' .. mw.ustring.lower(game) .. '-' .. prefix .. '-' .. index .. '-' .. mw.ustring.lower(name) | local indicatorName = gameNum .. '-' .. mw.ustring.lower(game) .. '-' .. prefix .. '-' .. index .. '-' .. mw.ustring.lower(name) | ||
local indicatorText = '[' .. '[' .. 'File:' .. | local indicatorText = '[' .. '[' .. 'File:' .. | ||
ucfirst(kind) .. ' icon ' .. game .. ' ' .. name .. | lang:ucfirst(kind) .. ' icon ' .. game .. ' ' .. name .. | ||
'.png|30px|' .. desc .. '|link=' .. link .. | '.png|30px|' .. desc .. '|link=' .. link .. | ||
']]' | ']]' | ||
Line 43: | Line 44: | ||
return '<indicator name="' .. indicatorName .. '">' .. indicatorText .. '</indicator>' | return '<indicator name="' .. indicatorName .. '">' .. indicatorText .. '</indicator>' | ||
else | else | ||
return | return frame:extensionTag('indicator', indicatorText, { name = indicatorName }) | ||
end | end | ||
end | end | ||
Line 112: | Line 113: | ||
--formatDate works same as #time, U is Unix time. Have to pass instance to pcall. | --formatDate works same as #time, U is Unix time. Have to pass instance to pcall. | ||
isValid, start = pcall( | --Passing empty string gets current time. | ||
isValid, start = pcall(lang.formatDate, lang, 'U', start) | |||
if not isValid then | if not isValid then | ||
return { { 'error', 'invalid date: ' .. dates[1] } } | return { { 'error', 'invalid date: ' .. dates[1] } } | ||
end | end | ||
isValid, finish = pcall( | isValid, finish = pcall(lang.formatDate, lang, 'U', finish) | ||
if not isValid then | if not isValid then | ||
return { { 'error', 'invalid date: ' .. dates[2] } } | return { { 'error', 'invalid date: ' .. dates[2] } } | ||
end | end | ||
local now = | start = tonumber(start) | ||
finish = tonumber(finish) | |||
local now = tonumber(lang:formatDate('U')) | |||
if start > finish or start > now or finish > now then | if start > finish or start > now or finish > now then | ||
return { { 'error', 'invalid date range: ' .. dates[1] .. ' to ' .. dates[2] } } | return { { 'error', 'invalid date range: ' .. dates[1] .. ' to ' .. dates[2] } } | ||
Line 127: | Line 131: | ||
local itemStart, itemFinish, nextItem | local itemStart, itemFinish, nextItem | ||
for i, item in ipairs(itemList) do | for i, item in ipairs(itemList) do | ||
itemStart = | itemStart = tonumber(lang:formatDate('U', item[1])) | ||
nextItem = itemList[i + 1] | nextItem = itemList[i + 1] | ||
if nextItem then | if nextItem then | ||
Line 134: | Line 138: | ||
nextItem = '' | nextItem = '' | ||
end | end | ||
itemFinish = | itemFinish = tonumber(lang:formatDate('U', nextItem)) | ||
--Note: this won't count being present for only the first or last day | --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 | if (start <= itemStart and finish > itemStart) or (start > itemStart and start < itemFinish) then | ||
Line 185: | Line 189: | ||
for i, game in ipairs(gameOrder) do | for i, game in ipairs(gameOrder) do | ||
code = code .. generateCode(argToTable(args[game]), game, i) | if args[game] then | ||
code = code .. generateCode(argToTable(args[game]), game, i) | |||
end | |||
end | end | ||
Latest revision as of 11:56, 1 August 2021
Documentation for this module may be created at Module:AgesEras/doc
local p = {}
local list = mw.loadData('Module:AgesEras/list')
local lang = mw.language.getContentLanguage()
local testMode = false
--arg names must match gameorder keys in /list module
--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 intentionally 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, gameNum)
local frame = mw.getCurrentFrame()
--Pad to two digits
gameNum = mw.ustring.format('%02d', gameNum)
local index = mw.ustring.format('%02d', data[1])
local item = data[2]
if not item[1] or not item[2] or not item[3] or item[4] then
return '<span class="error">' ..
'invalid ' .. mw.ustring.upper(game) .. ' ' .. kind .. ' data in [[Module:AgesEras/list]]' ..
'</span>'
end
game = mw.ustring.upper(game)
local gameFull = frame:expandTemplate({ title='abbrgame', args={ game, 'full' } })
local name = item[2]
local desc = item[3]
local prefix, link
if kind == 'game' then
name = 'NPO'
prefix = 'a' .. kind
kind = 'era'
link = 'New Pacific Order (' .. gameFull .. ')'
else
prefix = kind
link = 'History of the New Pacific Order in ' .. gameFull .. '#' .. desc
end
local indicatorName = gameNum .. '-' .. mw.ustring.lower(game) .. '-' .. prefix .. '-' .. index .. '-' .. mw.ustring.lower(name)
local indicatorText = '[' .. '[' .. 'File:' ..
lang:ucfirst(kind) .. ' icon ' .. game .. ' ' .. name ..
'.png|30px|' .. desc .. '|link=' .. link ..
']]'
if testMode then
return '<indicator name="' .. indicatorName .. '">' .. indicatorText .. '</indicator>'
else
return frame:extensionTag('indicator', indicatorText, { name = indicatorName })
end
end
--Returns { { start, end } or { error text }, ... }
local function argToTable(arg)
--This attempts to support most ways of writing ranges of dates, as long as they're consistent.
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 = '&'
elseif mw.ustring.find(arg, ',', 1, true) and (mw.ustring.find(arg, ' - ', 1, true) or mw.ustring.find(arg, ' to ', 1, true)) then
--Looks like potentially one range with commas in dates
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
--Returns { { age/era index, age/era object } or { 'error', error text }, ... }
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
local isValid
--formatDate works same as #time, U is Unix time. Have to pass instance to pcall.
--Passing empty string gets current time.
isValid, start = pcall(lang.formatDate, lang, 'U', start)
if not isValid then
return { { 'error', 'invalid date: ' .. dates[1] } }
end
isValid, finish = pcall(lang.formatDate, lang, 'U', finish)
if not isValid then
return { { 'error', 'invalid date: ' .. dates[2] } }
end
start = tonumber(start)
finish = tonumber(finish)
local now = tonumber(lang:formatDate('U'))
if start > finish or start > now or finish > now then
return { { 'error', 'invalid date range: ' .. dates[1] .. ' to ' .. dates[2] } }
end
local itemStart, itemFinish, nextItem
for i, item in ipairs(itemList) do
itemStart = tonumber(lang:formatDate('U', item[1]))
nextItem = itemList[i + 1]
if nextItem then
nextItem = nextItem[1]
else
nextItem = ''
end
itemFinish = tonumber(lang: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, { i, 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', { 1, gameList.game }, game, num)
end
for j, ageMatch in ipairs(findMatches(gameList.ages, range)) do
if ageMatch[1] == 'error' then
code = code .. '<span class="error">' .. ageMatch[2] .. '</span>'
else
code = code .. icon('age', ageMatch, game, num)
end
end
for k, eraMatch in ipairs(findMatches(gameList.eras, range)) do
if eraMatch[1] == 'error' then
code = code .. '<span class="error">' .. eraMatch[2] .. '</span>'
else
code = code .. icon('era', eraMatch, game, num)
end
end
end
end
return code
end
function p.main(frame)
return p.call(frame:getParent().args)
end
function p.call(args)
local code = ''
local gameOrder = list.gameorder
for i, game in ipairs(gameOrder) do
if args[game] then
code = code .. generateCode(argToTable(args[game]), game, i)
end
end
return code
end
function p.test(args)
testMode = true
return p.call(args)
end
return p