Module:Setlist
| This module uses TemplateStyles: |
| This module depends on the following other modules: |
This module implements {{setlist}}. Please see the template page for documentation.
local cfg = mw.loadData('Module:Setlist/configuration')
--------------------------------------------------------------------------------
-- Song class
--------------------------------------------------------------------------------
local Song = {}
Song.__index = Song
Song.fields = cfg.song_field_names
Song.cellMethods = {
number = 'makeNumberCell',
song = 'makeSongCell',
placeholder = 'makePlaceholderCell',
}
function Song.new(data)
local self = setmetatable({}, Song)
for k, v in pairs(data) do
self[k] = v
end
self.number = assert(tonumber(self.number), 'Song number must be numeric')
return self
end
function Song.makeSimpleCell(wikitext)
return mw.html.create('td')
:wikitext(wikitext or cfg.blank_cell)
end
function Song:makeNumberCell(ordered)
local display = ordered and '•' or string.format(cfg.number_terminated, self.number)
return mw.html.create('th')
:attr('id', string.format(cfg.song_id, self.number))
:attr('scope', 'row')
:wikitext(display)
end
function Song:makeSongCell()
local songCell = mw.html.create('td')
local songText = self.song and string.format(cfg.song_title, self.song) or cfg.untitled
songCell:wikitext(songText)
if self.note then
songCell:wikitext(string.format(cfg.note, self.note))
end
return songCell
end
function Song:makePlaceholderCell()
return mw.html.create('td')
:addClass('setlist-placeholder')
:wikitext(cfg.blank_cell)
end
function Song:exportRow(columns, ordered)
columns = columns or {}
local row = mw.html.create('tr')
for _, column in ipairs(columns) do
local method = Song.cellMethods[column]
if method and self[method] then
if column == 'number' then
row:node(self[method](self, ordered))
else
row:node(self[method](self))
end
end
end
return row
end
--------------------------------------------------------------------------------
-- Setlist class
--------------------------------------------------------------------------------
local Setlist = {}
Setlist.__index = Setlist
Setlist.fields = cfg.setlist_field_names
function Setlist.new(data)
local self = setmetatable({}, Setlist)
for field in pairs(Setlist.fields) do
self[field] = data[field]
end
-- Handle the 'ordered' parameter: only accept "no" (case-insensitive)
if type(data.ordered) == 'string' and data.ordered:lower() == 'no' then
self.ordered = true
else
self.ordered = false
end
self.songs = {}
self.encoreSongs = {}
self.encoreTwoSongs = {}
for _, songData in ipairs(data.songs or {}) do
local songObj = Song.new(songData)
if songData.intermission then
table.insert(self.songs, songObj)
elseif songData.encoretwo then
table.insert(self.encoreTwoSongs, songObj)
elseif songData.encore then
table.insert(self.encoreSongs, songObj)
else
table.insert(self.songs, songObj)
end
end
self.customHeaders = data.customHeaders or {}
return self
end
function Setlist:addEncoreSection(songs, label, tableRoot, columns, songColumnWidth)
if #songs == 0 then return end
local header = tableRoot:tag('tr'):addClass('setlist-subheader')
header:tag('th')
:addClass('setlist-number-header')
:attr('scope', 'col')
:wikitext(cfg.blank_cell)
header:tag('th')
:attr('scope', 'col')
:css('width', songColumnWidth)
:wikitext("''" .. label .. "''")
header:tag('th')
:addClass('setlist-placeholder-header')
:attr('scope', 'col')
:wikitext(cfg.placeholder)
for _, song in ipairs(songs) do
tableRoot:node(song:exportRow(columns, self.ordered))
end
end
function Setlist:__tostring()
local root = mw.html.create('div'):addClass('setlist')
local tableRoot = mw.html.create('table'):addClass('setlist')
if self.width then
tableRoot:css('width', self.width)
end
if self.headline then
tableRoot:tag('caption'):wikitext(self.headline)
end
local headerRow = tableRoot:tag('tr')
headerRow:tag('th')
:addClass('setlist-number-header')
:attr('scope', 'col')
:wikitext(cfg.number_abbr)
local columns = {'number', 'song', 'placeholder'}
local songColumnWidth = self.song_width or '95%'
headerRow:tag('th')
:attr('scope', 'col')
:css('width', songColumnWidth)
:wikitext(cfg.song)
headerRow:tag('th')
:addClass('setlist-placeholder-header')
:attr('scope', 'col')
:wikitext(cfg.placeholder)
for _, song in ipairs(self.songs) do
local sectionTitle = self.customHeaders[song.number]
if sectionTitle then
local sectionRow = tableRoot:tag('tr'):addClass('setlist-subheader')
sectionRow:tag('th')
:addClass('setlist-number-header')
:attr('scope', 'col')
:wikitext(cfg.blank_cell)
sectionRow:tag('th')
:attr('scope', 'col')
:css('width', songColumnWidth)
:wikitext("''" .. sectionTitle .. "''")
sectionRow:tag('th')
:addClass('setlist-placeholder-header')
:attr('scope', 'col')
:wikitext(cfg.placeholder)
end
if song.intermission then
local row = tableRoot:tag('tr'):addClass('setlist-subheader'):addClass('setlist-intermission')
row:tag('th')
:addClass('setlist-number-header')
:attr('scope', 'col')
:wikitext(cfg.blank_cell)
row:tag('th')
:attr('scope', 'col')
:css('width', songColumnWidth)
:wikitext("''Intermission''" .. (song.intermission_text and (" <small>(" .. song.intermission_text .. ")</small>") or ""))
row:tag('th')
:addClass('setlist-placeholder-header')
:attr('scope', 'col')
:wikitext(cfg.placeholder)
else
tableRoot:node(song:exportRow(columns, self.ordered))
end
end
local encoreLabel = self.encore_header or cfg.encore_header or 'Encore'
local encoreTwoLabel = self.encore_two_header or (encoreLabel .. ' 2')
self:addEncoreSection(self.encoreSongs, encoreLabel, tableRoot, columns, songColumnWidth)
self:addEncoreSection(self.encoreTwoSongs, encoreTwoLabel, tableRoot, columns, songColumnWidth)
if self.source then
local sourceRow = tableRoot:tag('tr'):addClass('setlist-source')
sourceRow:tag('th')
:attr('colspan', 2)
:attr('scope', 'row')
:tag('span')
:wikitext(cfg.source or 'Source:')
sourceRow:tag('td'):wikitext(self.source)
end
root:node(tableRoot)
return mw.getCurrentFrame():extensionTag{
name = 'templatestyles',
args = { src = cfg.style_src or 'Module:Setlist/styles.css' }
} .. tostring(root)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._main(args)
local data, songs, customHeaders = {}, {}, {}
local maxNum = 0
for k, v in pairs(args) do
if type(k) == 'string' then
local num = tonumber(k:match('(%d+)$'))
if num and v and v ~= '' then
if num > maxNum then maxNum = num end
songs[num] = songs[num] or {}
if k:match('^song%d+$') then
songs[num].song = v
elseif k:match('^note%d+$') then
songs[num].note = v
elseif k:match('^encore%d+$') then
songs[num].song = v
songs[num].encore = true
elseif k:match('^encore_note%d+$') then
songs[num].note = v
songs[num].encore = true
elseif k:match('^encoretwo%d+$') then
songs[num].song = v
songs[num].encoretwo = true
elseif k:match('^encoretwo_note%d+$') then
songs[num].note = v
songs[num].encoretwo = true
elseif k:match('^section%d+$') then
customHeaders[num] = v
elseif k:match('^intermission%d+$') then
local key = num - 0.5
songs[key] = {intermission = true, intermission_text = v, number = key}
else
data[k] = v
end
else
data[k] = v
end
end
end
data.songs = (function(t)
local ret = {}
for num, songData in pairs(t) do
songData.number = num
table.insert(ret, songData)
end
table.sort(ret, function(a, b) return a.number < b.number end)
return ret
end)(songs)
data.customHeaders = customHeaders
return tostring(Setlist.new(data))
end
function p.main(frame)
local args = require('Module:Arguments').getArgs(frame, {
wrappers = 'Template:Setlist 2'
})
return p._main(args)
end
return p