• Welcome to Touhou Wiki!
  • Registering is temporarily disabled. Check in our Discord server to request an account and for assistance of any kind.

Module:Lyrics

From Touhou Wiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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, '\n' .. 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 .. [=[">&nbsp;</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

-- [[Category:Lua Scripts|{{PAGENAME}}]]