Module:Lyrics
Appearance
Documentation for this module may be created at Module:Lyrics/doc
-- A port of [[Template:Lyrics]] to Lua
-- written by K
-- == HELPER FUNCTIONS ==
--[[ Returns true if the given value is defined and not empty. Used to replicate
the {{templateArg|}} notation used in MediaWiki template code. ]]
local function isset(target)
return target and target ~= ""
end
--[[ Search the list of template arguments for arguments matching the pattern
"album#", where # is a number, and then return them as an indexed table. ]]
local function scanForAlbums(frame)
local albumList = {}
local albumCount = 0
for k, v in frame:argumentPairs() do
if(string.find(k, '^album%d+$')) then
albumCount = albumCount + 1
end
end
for i=1,albumCount do
if(frame.args["album" .. i]) then table.insert(albumList, frame.args["album" .. i]) end
end
return albumList
end
--[[ Search the list of template arguments for arguments matching the patterns
"eng#", "kan#", or "rom#", where # is a number. Entries are sorted and
grouped together into appropriate tables within an indexed table. ]]
local function scanForStanzas(frame)
local stanzaList = {}
local stanzaCount = 0
if(isset(frame.args.eng_only)) then
local engStanzaCount = 0
for k, v in frame:argumentPairs() do
if(string.find(k, '^eng%d+$')) then
engStanzaCount = engStanzaCount + 1
end
end
stanzaCount = engStanzaCount
for i=1,stanzaCount do
local stanzaData = {}
if(frame.args["eng" .. i]) then stanzaData["eng"] = frame.args["eng" .. i]
else stanzaData["eng"] = "" end
table.insert(stanzaList, stanzaData)
end
else
local engStanzaCount = 0
local kanStanzaCount = 0
local romStanzaCount = 0
for k, v in frame:argumentPairs() do
if(string.find(k, '^eng%d+$')) then
engStanzaCount = engStanzaCount + 1
end
if(string.find(k, '^kan%d+$')) then
kanStanzaCount = kanStanzaCount + 1
end
if(string.find(k, '^rom%d+$')) then
romStanzaCount = romStanzaCount + 1
end
end
stanzaCount = math.max(engStanzaCount, kanStanzaCount, romStanzaCount)
for i=1,stanzaCount do
local stanzaData = {}
if(frame.args["eng" .. i]) then stanzaData["eng"] = frame.args["eng" .. i]
else stanzaData["eng"] = "" end
if(frame.args["kan" .. i]) then stanzaData["kan"] = frame.args["kan" .. i]
else stanzaData["kan"] = "" end
if(frame.args["rom" .. i]) then stanzaData["rom"] = frame.args["rom" .. i]
else stanzaData["rom"] = "" end
table.insert(stanzaList, stanzaData)
end
end
return stanzaList
end
--[[ Creates the sort key used for {{DEFAULTSORT}}. A sort key is created by
taking the English title, romanized title, or Japanese title (takes the
first it can find), setting it to lowercase, and capitalizing the first
letter in the title. ]]
local function calculateDefaultSort(frame)
local sortkey = ""
if(isset(frame.args.titleen)) then
sortkey = frame.args.titleen
elseif(isset(frame.args.titlerom)) then
sortkey = frame.args.titlerom
elseif(isset(frame.args.titlejp)) then
sortkey = frame.args.titlejp
end
-- lower all, uppercase first
sortkey = string.gsub(string.lower(sortkey), '^%l', string.upper)
if(isset(sortkey)) then
return "{{DEFAULTSORT:" .. sortkey .. "}}"
else
return ""
end
end
-- == PAGE SECTIONS ==
--[[ Generates the header, which contains the title and group name. ]]
local function lyricsHeaderRow(frame)
local headerRow = {}
table.insert(headerRow, [=[ <tr>
<th class="incell_top" style="font-weight: normal;" colspan="]=])
-- colspan
if(isset(frame.args.eng_only)) then
table.insert(headerRow, 2)
else
table.insert(headerRow, 3)
end
table.insert(headerRow, [=[">''']=])
-- display title
if(isset(frame.args.titleen)) then
table.insert(headerRow, frame.args.titleen)
else
table.insert(headerRow, frame.args.titlejp)
end
table.insert(headerRow, [=[''']=])
-- group
if(isset(frame.args.group)) then
table.insert(headerRow, " by " .. frame.args.group)
end
table.insert(headerRow, [=[</th>
</tr>]=])
return table.concat(headerRow)
end
--[[ Generates the main info section, which contains song info as well as a
gallery of albums the song is featured on.
Uses scanForAlbums(frame).]]
local function lyricsInfoRow(frame)
local infoRow = {}
table.insert(infoRow, [=[ <tr>
<td]=])
if(not isset(frame.args.eng_only)) then
table.insert(infoRow, [=[ colspan="2"]=])
end
table.insert(infoRow, [=[>]=])
-- yes, these newlines are required for the MW parser to correctly interpret start-of-line entities like * or # for lists
if(isset(frame.args.titleen) and isset(frame.args.titlejp)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*'''{{lang|ja|]=] .. frame.args.titlejp .. [=[}}''']=]))
end
if(isset(frame.args.titlerom)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*'']=] .. frame.args.titlerom .. [=['']=]))
end
if(isset(frame.args.length)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*length: ]=] .. frame.args.length))
end
if(isset(frame.args.arranger)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*arrangement: ]=] .. frame.args.arranger))
end
if(isset(frame.args.lyricist)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*lyrics: ]=] .. frame.args.lyricist))
end
if(isset(frame.args.vocalist)) then
table.insert(infoRow, frame:preprocess('\n' .. [=[*vocals: ]=] .. frame.args.vocalist))
end
if(isset(frame.args.other_staff)) then
table.insert(infoRow, frame:preprocess('\n' .. frame.args.other_staff))
end
if(isset(frame.args.source)) then
table.insert(infoRow, frame:preprocess('\n' .. frame.args.source))
end
table.insert(infoRow, '\n' .. [=[</td>
<td>]=])
-- now come the albums
local albumList = scanForAlbums(frame)
if(#albumList > 0) then
table.insert(infoRow, "Featured in: \n:")
for _, albumTemplate in ipairs(albumList) do
table.insert(infoRow, frame:preprocess(albumTemplate) .. " ")
end
end
table.insert(infoRow, [=[</td>
</tr>]=])
return table.concat(infoRow)
end
--[[ Generates the extra info section, which contains extra information as
specified in the extra_info argument. ]]
local function lyricsExtraInfoRow(frame)
local extraInfoRow = {}
if(isset(frame.args.extra_info)) then
local colspan = 0
if(isset(frame.args.eng_only)) then
colspan = 2
else
colspan = 3
end
table.insert(extraInfoRow, [=[
<tr>
<th class="incell" colspan="]=] .. colspan .. [=[">Additional Info</th>
</tr>
<tr>
<td style="text-align: justify" colspan="]=] .. colspan .. [=[">]=])
table.insert(extraInfoRow, frame:preprocess(frame.args.extra_info))
table.insert(extraInfoRow, [=[</td>
</tr>]=])
end
return table.concat(extraInfoRow)
end
--[[ Generates stanzas upon stanzas of lyrics.
Uses scanForStanzas(frame)]]
local function lyricsStanzaRows(frame)
local stanzaRows = {}
-- header row for stanzas
table.insert(stanzaRows, [=[ <tr>
]=])
if(isset(frame.args.eng_only)) then
table.insert(stanzaRows, [=[<th class="incell" colspan="2">English</th>]=])
else
table.insert(stanzaRows, [=[<th class="incell" style="width: 30%">Original</th>
<th class="incell" style="width: 35%">Romanized</th>
<th class="incell" style="width: 35%">Translation</th>]=])
end
table.insert(stanzaRows, [=[
</tr>]=])
-- stanzas ahoy
local stanzaList = scanForStanzas(frame)
for i, stanza in ipairs(stanzaList) do
table.insert(stanzaRows, [=[
<tr class="lyrics_row">]=])
if(isset(frame.args.eng_only)) then
table.insert(stanzaRows, [=[<td>
]=] .. stanza["eng"] .. '\n' .. [=[</td>]=])
else
table.insert(stanzaRows, [=[<td lang="ja" xml:lang="ja">
]=] .. stanza["kan"] .. '\n' .. [=[</td>]=])
table.insert(stanzaRows, [=[<td>
]=] .. stanza["rom"] .. '\n' .. [=[</td>]=])
table.insert(stanzaRows, [=[<td>
]=] .. stanza["eng"] .. '\n' .. [=[</td>]=])
end
table.insert(stanzaRows, [=[</tr>]=])
end
return table.concat(stanzaRows)
end
--[[ Generates the notes sections, based on the notes argument. ]]
local function lyricsNotesRow(frame)
local notesRow = {}
if(isset(frame.args.notes)) then
local colspan = 0
if(isset(frame.args.eng_only)) then
colspan = 2
else
colspan = 3
end
table.insert(notesRow, [=[
<tr>
<th class="incell" colspan="]=] .. colspan .. [=["> </th>
</tr>
<tr>
<td style="text-align: justify" colspan="]=] .. colspan .. [=[">]=])
table.insert(notesRow, frame:preprocess(frame.args.notes))
table.insert(notesRow, [=[</td>
</tr>]=])
end
return table.concat(notesRow)
end
--[[ Generates the shaded row at the bottom of the lyric sheet. ]]
local function lyricsClosingRow(frame)
local closingRow = {}
local colspan = 0
if(isset(frame.args.eng_only)) then
colspan = 2
else
colspan = 3
end
table.insert(closingRow, [=[
<tr>
]=])
if(isset(frame.args.notes)) then
table.insert(closingRow, [=[<td class="incell_bottom" colspan="]=] .. colspan .. [=["></td>]=])
else
if(isset(frame.args.eng_only)) then
table.insert(closingRow, [=[<td class="incell_bottom" colspan="2"></td>]=])
else
table.insert(closingRow, [=[<td class="incell_bottomleft"></td>
<td class="incell"></td>
<td class="incell_bottomright"></td>]=])
end
end
table.insert(closingRow, [=[
</tr>]=])
return table.concat(closingRow)
end
--[[ Categorizes the article based on its transcription/translation statuses.
Uses calculateDefaultSort(frame). ]]
local function lyricsCategories(frame)
local categories = {}
table.insert(categories, frame:preprocess(calculateDefaultSort(frame)))
table.insert(categories, [=[[[Category:Lyrics]]]=])
if(isset(frame.args.eng_only)) then
if(isset(frame.args.untranscribed)) then
table.insert(categories, [=[[[Category:Untranscribed/Lyrics]]]=])
else
table.insert(categories, [=[[[Category:Lyrics in English]]]=])
end
else
if(isset(frame.args.untranscribed)) then
table.insert(categories, [=[[[Category:Untranscribed/Lyrics]]]=])
else
table.insert(categories, [=[[[Category:Lyrics in Kanji]]]=])
end
if(isset(frame.args.unromanized)) then
table.insert(categories, [=[[[Category:Unromanized/Lyrics]]]=])
end
if(isset(frame.args.untranslated)) then
table.insert(categories, [=[[[Category:Untranslated/Lyrics]]]=])
end
end
return table.concat(categories, '\n')
end
-- == PUBLIC FUNCTIONS (see export table at bottom) ==
--[[ Assembles the whole template for a Lyrics page. ]]
local function outputLyrics(frame)
local template = {""}
table.insert(template, '<table class="template_lyrics outcell">')
table.insert(template, lyricsHeaderRow(frame))
table.insert(template, lyricsInfoRow(frame))
table.insert(template, lyricsExtraInfoRow(frame))
table.insert(template, lyricsStanzaRows(frame))
table.insert(template, lyricsNotesRow(frame))
table.insert(template, lyricsClosingRow(frame))
table.insert(template, '</table>')
table.insert(template, lyricsCategories(frame))
return table.concat(template, '\n')
end
--[[ Wrapper for outputLyrics(frame) for use with wrapper templates.
This is probably the most common entry point of the module. ]]
local function outputLyricsFromTemplate(frame)
-- use parent frame, as this function is intended to be called from a wrapper template
return outputLyrics(frame:getParent())
end
-- table of local functions to expose to the public
export = {
outputLyrics = outputLyrics,
outputLyricsFromTemplate = outputLyricsFromTemplate
}
return export