- Welcome to Touhou Wiki!
- Registering is temporarily disabled. Check in our Discord server to request an account and for assistance of any kind.
Module:Lyrics2
Jump to navigation
Jump to search
Documentation for this module may be created at Module:Lyrics2/doc
Script error: Lua error at line 402: invalid escape sequence near ''['.
-- A port of [[Template:Lyrics]] to Lua
-- written by K
-- rewritten by DennouNeko
-- == HELPER FUNCTIONS ==
require("Module:Common")
common.romanizable = {'zh-hans', 'zh-hant', 'zh-cn', 'zh-tw', 'zh-hk', 'zh-sg', 'zh-mo', 'zh-my', 'ja', 'ko', 'vi'}
-- Note: There's a full-width space to space conversion. Don't remove it!
common.normalization_table = {[' '] = ' ', ['~'] = '~', ['!'] = '!', ['?'] = '?'}
-- "Module:Common" candidate
--[[ Tries to get contents of a page with given name.
If the function fails it returns nil (page doesn't exist or can't be loaded)
On success it returns the contents of page (it can be be partially preprocessed, so watch out for parser markers). ]]
function mw.get_page(name)
if not isset(name) then return nil end
local frame = mw.getCurrentFrame()
-- do a safe call to catch possible errors, like "template doesn't exist"
local stat,page = pcall(frame.expandTemplate, frame, {title = ':' .. name})
if not stat then
-- TODO: 'page' contains the error message. Do some debugging?
return nil
end
return page
end
-- "Module:Common" candidate
--[[ Sort table and remove repeating elements.
Since tbl is passed as reference, any changes in it will affect the passed table. ]]
function table.trunk(tbl)
table.sort(tbl)
local last
local redo
repeat
redo = false
last = nil
for k,v in pairs(tbl) do
if v ~= last then
last = v
else
table.remove(tbl, k)
redo = true
break
end
end
until not redo
end
-- "Module:Common" candidate?
--[[ simulates the (a ? b : c) notation of C/C++ and PHP languages.
Similar to {{#if:{{{a|}}}|{{{b|}}}|{{{c|}}}}} from parser functions. ]]
function condval(a, b, c)
if a then return b else return c end
end
-- "Module:Common" candidate
--[[ Checks if a given value is among the elements of a table and returns its index.
Returns nil if it can't find it. ]]
function table.in_table(tbl, val)
for k,v in pairs(tbl) do
if v == val then return k end
end
return nil
end
-- "Module:Common" candidate?
--[[
Builds a simple HTML table from a 'tbl' elements with 'options' as a list of options for table.
Useful for quick tests or if a complex table is not needed.
Currently available options are:
* cols - max column count
* class - text containing classes that should be added to the table
* style - text that should be added to the table's style (like "empty-cells: hide;")
* spacing - separation of table cells
--]]
function table.build_table(tbl, options)
local cols = nil
local ret = {}
-- to avoid script crashing when skipping options in param list
if options == nil then options = {} end
if options['cols'] ~= nil then cols = tonumber(options['cols']) end
ret[#ret+1] = '<table'
if isset(options['class']) then ret[#ret+1] = ' class="' .. options['class'] .. '"' end
if isset(options['style']) then ret[#ret+1] = ' style="' .. options['style'] .. '"' end
if isset(options['spacing']) then ret[#ret+1] = ' cellspacing="' .. options['spacing'] .. '"' end
ret[#ret+1] = '>'
local colnum = 0
if cols ~= nil then
-- build a table with max 'cols' columns in row, content of cells are the values of 'tbl' elements
ret[#ret+1] = '<tr>'
for k,v in pairs(tbl) do
if colnum > 0 and math.mod(colnum, cols) == 0 then
ret[#ret+1] = '</tr><tr>'
end
ret[#ret+1] = '<td><!--' .. k .. '-->\n' .. v .. '</td>'
colnum = colnum + 1
end
if cols ~= nil then
while math.mod(colnum, cols) ~= 0 do
ret[#ret+1] = '<td></td>'
colnum = colnum + 1
end
end
ret[#ret+1] = '</tr>'
else
-- build a simple table with 'key | value' rows
for k,v in pairs(tbl) do
ret[#ret+1] = '<tr><td>\n' .. k .. '</td><td>\n' .. v .. '</td></tr>'
end
end
ret[#ret+1] = '</table>'
return table.concat(ret)
end
-- "Module:Common" candidate
--[[Removes HTML tags from string. ]]
function string.strip_tags(text)
if isset(text) then
local tmp
repeat
tmp = text
-- a pair of tags, like <td style="">...</td>
text = string.gsub(text, '<%s*(%w+).->(.-)<%s*/%s*%1%s*>', '%2')
-- closed tag, like <br/>
text = string.gsub(text, '<%s*%w+%s*/%s*>', '')
until tmp == text
end
return text
end
-- "Module:Common" candidate
--[[ Compare 'n' elements in two tables, starting from 's1' in first table and 's2' in second table. ]]
function table.partial_compare(t1, t2, s1, s2, n)
if n < 1 then return true end -- basically there's nothing to compare, so no differences were found
for i = 0,(n-1) do
-- Note that nil values are also valid.
if t1[s1+i] ~= t2[s2+i] then return false end
end
return true
end
-- "Module:Lib utf8" candidate (required by utf8explode)
--[[ Iterator that returns the start, end and the current character.
Required by string.utf8explode ]]
function string.utf8iter(str)
local i = 1
local j
local n = #str
local char = nil
return function()
if i <= n then
j = i
char = str:byte(j)
if char < 0x80 then
-- 0xxxxxxx
i = j + 1
elseif char < 0xc0 then
-- 10xxxxxx
return nil -- error, we're in the middle of a character
elseif char < 0xe0 then
-- 110xxxxx 10xxxxxx
i = j + 2
elseif char < 0xf0 then
-- 1110xxxx 10xxxxxx 10xxxxxx
i = j + 3
elseif char < 0xf8 then
-- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
i = j + 4
elseif char < 0xfc then
-- 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
i = j + 5
elseif char < 0xfe then
-- 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
i = j + 6
else
return nil -- 0xfe and 0xff are invalid UTF-8 values
end
-- TODO: parsing of a character?
return j,(i-1),string.sub(str, j, (i-1))
end
end
end
-- "Module:Lib utf8" candidate (requires utf8iter)
--[[ Splits an UTF-8 text (encoding used by Lua) into single character parts. ]]
function string.utf8explode(text)
local pts = {}
if text == nil or type(text) ~= 'string' then return pts end
if #text > 0 then
for s,e,v in string.utf8iter(text) do pts[#pts+1] = v end
else
-- technically there is one part - the empty string
pts[#pts+1] = ''
end
return pts
end
-- "Module:Lib utf8" candidate (requires utf8explode)
--[[ Replaces whole non-ASCII characters.
'reps' should be a table in format ['find'] = 'replace', like
{['A'] = 'a', ['B'] = b, ['犬'] = '猫', ...},
where table keys have to be single characters and the replacement can be any string. ]]
function string.utf8replace_char(text, reps)
local parts = string.utf8explode(text)
for k,v in pairs(parts) do
if isset(reps[v]) then parts[k] = reps[v] end
end
return table.concat(parts)
end
-- "Module:Lib utf8" candidate (requires utf8explode)
--[[ Replaces non-ASCII strings.
'reps' should be a table of string pairs, like:
{ {'find', 'replace'}, {'bird', 'cat'}, {'fly', 'walk'}, {'八雲 藍', '式神'}, ... }
Only the first found replacement is being executed.
Note that this is relatively slow solution, so should be used only for replacement of non-ASCII texts.
For ASCII text it's better to use string.gsub ]]
function string.utf8replace(text, reps)
local parts = string.utf8explode(text)
local ret = {}
local reps2 = {}
local found
for k,v in pairs(reps) do reps2[#reps2+1] = {string.utf8explode(v[1]), string.utf8explode(v[2])} end
local i = 1
while i <= #parts do
found = false
for k,v in pairs(reps2) do
if table.partial_compare(parts, v[1], i, 1, #v[1]) then
found = true
-- found match, perform swap
for k1,v1 in pairs(v[2]) do ret[#ret+1] = v1 end
i = i + #v[1] - 1
break
end
end
if not found then
ret[#ret+1] = parts[i]
end
i = i + 1
end
return table.concat(ret)
end
--[[ Search the list of template arguments for arguments matching the pattern
"album#", where # is a number, and then return them as an indexed, sorted table.]]
local function scanForAlbums(frame)
local idx = {}
local args = {}
local albumList = {}
for k, v in frame:argumentPairs() do
s,e,t = string.find(k, '^album(%d+)$')
if s ~= nil then
idx[#idx+1] = tonumber(t)
args[#idx] = v
end
end
table.sort(idx)
for k,v in pairs(idx) do
albumList[#albumList + 1] = args[k]
end
return albumList
end
--[[ Extracts list of names from a albumList.
Indexes of found names match indexes in album table. ]]
local function getAlbumNames(albumList)
local cats = {}
local found
local link
for k,v in pairs(albumList) do
found = nil
link = nil
s,e,t = string.find(v, '%[%[(.+)%]%]')
if s ~= nil then
tl = string.explode(t, '|')
for k1,v1 in pairs(tl) do
s,e,t = string.find(v1, '^%s-link%s-=%s-(.+)%s-$')
if s ~= nil then
-- found a image tag
found = t
break
end
end
if found == nil then
-- not an image link then assume it's usual link, so get the last part as album name, first part as link
if #tl > 0 then
found = tl[#tl]
if #tl > 1 then link = tl[1] end
end
end
else
-- TODO: not a link - assume it's a name?
-- found = t
end
if found ~= nil then
cats[#cats+1] = {name = string.gsub(found, '_', ' '), link = link, idx = k}
end
end
return cats
end
--[[ Partial name normalization. ]]
local function normalize(text)
if isset(text) then
-- full-width characters to ASCII equivalents
text = string.utf8replace_char(text, common.normalization_table)
-- remove any HTML tags
text = string.strip_tags(text)
-- any whitespace chain into single space
text = string.gsub(text, '%s+', ' ')
-- remove preceding and trailing spaces
text = string.trim(text)
end
return text
end
--[[ Loads and parses a JavaScript table "song_info" from a script with given name. ]]
local function load_info(name)
local tstr = mw.get_page(name)
if tstr == nil then return ret end
local ret = {}
local s,e,t,t1,t2
s,e,t = string.find(tstr, 'var%s+song_info%s-=%s-{(.-)};')
if s ~= nil then
for n,lst in string.gmatch(t, '"(.-[^\\])"%s-:%s-{(.-)}') do
n = normalize(n)
ret[n] = {}
for t1,t2 in string.gmatch(lst, '"(.-[^\\])"%s-:%s-"(.-[^\\])"') do
t1 = normalize(t1)
-- unescape the string
ret[n][t1] = string.gsub(t2, '\\(.)', {['\\'] = '\\', ['n'] = '\n', ['"'] = '"'})
end
end
else
error('song_info not found!')
end
return ret
end
--[[ Search for "source: " and "original title: ", add a hover text to title and a "notes: " below. ]]
local function wrap_titles(frame, elems, info_list)
local source = nil
local title,titlek,titlen = nil,nil,nil
local s,e,t,t1,t2,tt
for k,v in pairs(elems) do
s,e,t1,t = string.find(v, '^(%*%s*original title:%s*)(.+)$')
if s ~= nil then
title = t
titlen = normalize(t)
titlek = k
tt = t1
else
s,e,t = string.find(v, '^%*%s*source:%s*(.*)$')
if s ~= nil then
s,e,t1 = string.find(t, '%[%[.+|(.+)%]%]')
if s == nil then s,e,t1 = string.find(t, '%[%[(.+)%]%]') end
if s ~= nil then t = t1 end
source = normalize(t)
end
end
if isset(source) and isset(titlen) then break end
end
if isset(source) and isset(titlen) then
if info_list[source] ~= nil then
if isset(info_list[source][titlen]) then
local extra = info_list[source][titlen]
s,e,t,t1 = string.find(extra, '^(.*)%s-%[(.+)%]')
if s == nil then t = extra end
if s ~= nil and isset(t1) then
local lst = string.explode(t1, ';')
if mw.exists(lst[#lst]) then
lst[#lst] = '[\[' .. lst[#lst] .. ']]'
end
extra = table.concat(lst,'; ')
elems[#elems+1] = '* notes: ' .. extra
end
if isset(t) then
elems[titlek] = tt .. frame:expandTemplate{title = 'h:title', args = {title, t} }
end
end
end
end
end
--[[ Add links for staff where linking is possible. ]]
local function link_staff(frame, elems)
-- nothing to do for now
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 idx = {}
local eng = {}
local kan = {}
local rom = {}
local cols = {['colnum'] = 0, ['single'] = false, ['full'] = false, ['eng'] = false, ['rom'] = false, ['kan'] = false}
stanzaList = {}
for k, v in frame:argumentPairs() do
s,e,t = string.find(k, '^eng(%d+)$')
if s ~= nil then
local i = tonumber(t)
idx[#idx+1] = i
eng[i] = v
cols['eng'] = true
end
s,e,t = string.find(k, '^kan(%d+)$')
if s ~= nil then
local i = tonumber(t)
idx[#idx+1] = i
kan[i] = v
cols['kan'] = true
end
s,e,t = string.find(k, '^rom(%d+)$')
if s ~= nil then
local i = tonumber(t)
idx[#idx+1] = i
rom[i] = v
cols['rom'] = true
end
end
-- sort indexes and remove repeating ones
table.trunk(idx)
for k,v in pairs(idx) do
local tmp = {}
tmp['eng'] = condval(isset(eng[v]), eng[v], ' ')
tmp['rom'] = condval(isset(rom[v]), rom[v], ' ')
tmp['kan'] = condval(isset(kan[v]), kan[v], ' ')
stanzaList[#stanzaList+1] = tmp
end
if cols['eng'] then cols['colnum'] = cols['colnum'] + 1 end
if cols['rom'] then cols['colnum'] = cols['colnum'] + 1 end
if cols['kan'] then cols['colnum'] = cols['colnum'] + 1 end
cols['single'] = (cols['colnum'] < 2)
cols['full'] = (cols['colnum'] > 2)
return stanzaList, cols
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
return condval(isset(sortkey), "{\{DEFAULTSORT:" .. string.gsub(string.lower(sortkey), '^%l', string.upper) .. "}}", "")
end
-- == PAGE SECTIONS ==
--[[ Generates the header, which contains the title and group name. ]]
local function lyricsHeaderRow(frame, cols)
local hr = {}
hr[#hr+1] = '<tr>\n<th class="incell_top" style="font-weight: normal;" colspan="' .. cols['colnum'] .. '">'
-- display title
hr[#hr+1] = "'''" .. condval(frame.args.titleen, frame.args.titleen, frame.args.titlejp) .. "'''"
if isset(frame.args.group) then hr[#hr+1] = " by " .. frame.args.group end
hr[#hr+1] = '</th>\n</tr>'
return table.concat(hr)
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, cols, albumList, albumNames, info_list)
local ir = {}
local cc = {}
ir[#ir+1] = '<tr>\n<td colspan="' .. cols['colnum'] .. '">'
-- albums first, since it's a floating frame
if #albumList > 0 then
local alist = mw.clone(albumList)
local lnk
for k,v in pairs(albumNames) do
if isset(alist[v.idx]) then
local link = v.name
if isset(v.link) then link = v.link .. '|' .. v.name end
alist[v.idx] = alist[v.idx] .. '<br/>[\[' .. link .. ']]'
end
end
ir[#ir+1] = '\n<div style="float: right; clear: right;">'
ir[#ir+1] = "Featured in: \n:"
ir[#ir+1] = table.build_table(alist, {cols = 3, spacing = 4, style="text-align: center;"})
ir[#ir+1] = '</div>'
end
-- 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
cc[#cc+1] = frame:preprocess("\n*'''{{lang|ja|" .. frame.args.titlejp .. "}}'''")
end
if isset(frame.args.titlerom) then
cc[#cc+1] = frame:preprocess("\n*''" .. frame.args.titlerom .. "''")
end
if isset(frame.args.length) then
cc[#cc+1] = frame:preprocess("\n*length: " .. frame.args.length)
end
if isset(frame.args.arranger) then
cc[#cc+1] = frame:preprocess("\n*arrangement: " .. frame.args.arranger)
end
if isset(frame.args.lyricist) then
cc[#cc+1] = frame:preprocess("\n*lyrics: " .. frame.args.lyricist)
end
if isset(frame.args.vocalist) then
cc[#cc+1] = frame:preprocess("\n*vocals: " .. frame.args.vocalist)
end
if isset(frame.args.other_staff) then
cc[#cc+1] = frame:preprocess("\n" .. frame.args.other_staff)
end
if isset(frame.args.source) then
cc[#cc+1] = frame:preprocess("\n" .. frame.args.source)
end
local elems = string.explode(table.concat(cc), '\n') -- to make sure that it's a "one element per line" table
-- process the elements
link_staff(frame, elems)
wrap_titles(frame, elems, info_list)
ir[#ir+1] = table.concat(elems, '\n') -- assemble it back into a string
-- just to make sure that everything stays in the cell
ir[#ir+1] = '<div style="float: none; clear: both;></div>'
ir[#ir+1] = "</td>\n</tr>"
return table.concat(ir)
end
--[[ Generates the extra info section, which contains extra information as
specified in the extra_info argument. ]]
local function lyricsExtraInfoRow(frame, cols)
local eir = {}
if isset(frame.args.extra_info) then
eir[#eir+1] = '\n<tr>\n<th class="incell" colspan="' .. cols['colnum'] .. '">Additional Info</th>\n</tr>'
eir[#eir+1] = '\n<tr>\n<td style="text-align: justify" colspan="' .. cols['colnum'] .. '">'
eir[#eir+1] = frame:preprocess('\n' .. frame.args.extra_info)
eir[#eir+1] = '</td>\n</tr>'
end
return table.concat(eir)
end
--[[ Generates stanzas upon stanzas of lyrics.
Uses scanForStanzas(frame)]]
local function lyricsStanzaRows(frame, cols, slist)
local srow = {}
local lang = 'ja'
if isset(frame.args['lang']) then
lang = condval(frame.args['lang'] == 'none', '', frame.args['lang'])
end
-- header row for stanzas
srow[#srow+1] = '\n<tr>'
if cols['kan'] then
srow[#srow+1] = '\n<th class="incell"'
if not cols['single'] then srow[#srow+1] = ' style="width: 30%"' end
srow[#srow+1] = '>Original</th>'
end
if cols['rom'] then
srow[#srow+1] = '\n<th class="incell"'
if not cols['single'] then srow[#srow+1] = ' style="width: 35%"' end
srow[#srow+1] = '>Romanized</th>'
end
if cols['eng'] then
srow[#srow+1] = '\n<th class="incell"'
if not cols['single'] then srow[#srow+1] = ' style="width: 35%"' end
srow[#srow+1] = condval(cols['single'], '>English</th>', '>Translation</th>')
end
srow[#srow+1] = '\n</tr>'
for i, stanza in pairs(slist) do
csused = false
srow[#srow+1] = '\n<!-- row:' .. i .. '-->\n<tr class="lyrics_row">'
if cols['kan'] then
srow[#srow+1] = '<td'
if(isset(lang)) then srow[#srow+1] = ' lang="' .. lang .. '" xml:lang="' .. lang .. '"' end
srow[#srow+1] = '>\n' .. stanza['kan'] .. '\n</td>'
end
if cols['rom'] then
srow[#srow+1] = '<td>\n' .. stanza['rom'] .. '\n</td>'
end
if cols['eng'] then
srow[#srow+1] = '<td>\n' .. stanza['eng'] .. '\n</td>'
end
srow[#srow+1] = '\n</tr>'
end
if isset(frame.args['lyrics_source']) then
srow[#srow+1] = '<tr><td colspan="' .. cols['colnum'] .. '" style="text-align: center; font-size: 83%;">'
srow[#srow+1] = "\n:''Lyrics source: " .. frame.args['lyrics_source'] .. "''"
srow[#srow+1] = '</td></tr>'
end
return table.concat(srow)
end
--[[ Generates the notes sections, based on the notes argument. ]]
local function lyricsNotesRow(frame, cols)
local nrow = {}
if isset(frame.args['notes']) then
nrow[#nrow+1] = '\n<tr><th class="incell" colspan="' .. cols['colnum'] .. '"></th></tr>'
nrow[#nrow+1] = '\n<tr><td style="text-align: justify" colspan="' .. cols['colnum'] .. '">'
nrow[#nrow+1] = frame:preprocess('\n' .. frame.args['notes'])
nrow[#nrow+1] = '\n</td></tr>'
cols['close_single'] = true
end
return table.concat(nrow)
end
--[[ Generates the shaded row at the bottom of the lyric sheet. ]]
local function lyricsClosingRow(frame, cols)
local crow = {}
crow[#crow+1] = '\n<tr>\n'
if cols['close_single'] then
crow[#crow+1] = '<td class="incell_bottom" colspan="' .. cols['colnum'] .. '"></td>'
else
if cols['single'] then
crow[#crow+1] = '<td class="incell_bottom"></td>'
else
crow[#crow+1] = '<td class="incell_bottomleft"></td>'
if cols['full'] then crow[#crow+1] = '<td class="incell"></td>' end
crow[#crow+1] = '<td class="incell_bottomright"></td>'
end
end
crow[#crow+1] = '\n</tr>'
return table.concat(crow)
end
--[[ Categorizes the article based on its transcription/translation statuses.
Uses calculateDefaultSort(frame). ]]
local function lyricsCategories(frame, cols, albumNames)
local cats = {''} -- so the first category won't disappear
local romanizable = not isset(frame.args['lang']) or (table.in_table(common.romanizable, frame.args['lang']) ~= nil)
local skip_rom = false
if isset(frame.args['eng_only']) then skip_rom = true end
if isset(frame.args['lang']) and not romanizable then skip_rom = true end
cats[#cats+1] = frame:preprocess(calculateDefaultSort(frame))
-- escaping the second '[', so parser won't assume it's a category
cats[#cats+1] = '[\[Category:Lyrics]]'
if isset(frame.args['group']) then
-- extract the group name from links
s,e,t = string.find(frame.args['group'], '%[%[.+|(.+)%]%]')
-- in case of simple links
if s == nil then s,e,t = string.find(frame.args['group'], '%[%[(.+)%]%]') end
-- TODO: not sure if I can assume that group has category if the "group" param wasn't a link...
-- if s == nil then t = frame.args['group'] end
if isset(t) then cats[#cats+1] = '[\[Category:' .. t .. ']]' end
end
for k,v in pairs(albumNames) do
-- TODO: scan album pages for categories?
cats[#cats+1] = '[\[Category:' .. v['name'] .. ' Album]]'
end
if cols['eng'] then
cats[#cats+1] = '[\[Category:Lyrics in English]]'
else
cats[#cats+1] = '[\[Category:Untranslated/Lyrics]]'
end
if cols['kan'] then
if romanizable then cats[#cats+1] = '[\[Category:Lyrics in Kanji]]' end
elseif not isset(frame.args['eng_only']) then
cats[#cats+1] = '[\[Category:Untranscribed/Lyrics]]'
end
if not cols['rom'] and not skip_rom then
cats[#cats+1] = '[\[Category:Unromanized/Lyrics]]'
end
-- MediaWiki would ignore the repeating categories anyway, but just to be safe...
table.trunk(cats)
return table.concat(cats, '\n')
end
-- == PUBLIC FUNCTIONS (see export table at bottom) ==
--[[ Assembles the whole template for a Lyrics page. ]]
local function outputLyrics(frame)
local tpl = {""}
tpl[#tpl+1] = '<table class="template_lyrics outcell">'
-- first prepare all the necessary data
local albumList = scanForAlbums(frame)
local albumNames = getAlbumNames(albumList)
local slist,cols = scanForStanzas(frame)
local info_list = load_info('MediaWiki:SongSource.js')
-- allows to force a single-cell closing row
cols['close_single'] = false
tpl[#tpl+1] = lyricsHeaderRow(frame, cols)
tpl[#tpl+1] = lyricsInfoRow(frame, cols, albumList, albumNames, info_list)
tpl[#tpl+1] = lyricsExtraInfoRow(frame, cols)
tpl[#tpl+1] = lyricsStanzaRows(frame, cols, slist)
tpl[#tpl+1] = lyricsNotesRow(frame, cols)
tpl[#tpl+1] = lyricsClosingRow(frame, cols)
tpl[#tpl+1] = '</table>'
tpl[#tpl+1] = lyricsCategories(frame, cols, albumNames)
return table.concat(tpl, '\n')
end
local function dump_info(frame)
local ret = {''}
lst = load_info(frame.args['name'])
ret[#ret+1] = '<table class="wikitable" style="width: 100%;">'
for k,v in pairs(lst) do
ret[#ret+1] = '<tr><th colspan="2" style="text-align: center;">' .. k .. '</th></tr>'
for k1,v1 in pairs(v) do
ret[#ret+1] = '<tr><td>' .. k1 .. '</td><td>' .. v1 .. '</td></tr>'
end
end
ret[#ret+1] = '</table>'
return table.concat(ret, '\n')
end
local function uni_test(frame)
local text = frame.args[1]
local search = frame.args['search']
local replace = frame.args['replace']
if isset(text) then
if isset(search) and isset(replace) then
return string.utf8replace_char(text, {[search] = replace})
else
local pts = {''}
for s,e,v in string.utf8iter(text) do
pts[#pts+1] = '* (' .. s .. ',' .. e .. ") = '" .. v .. "'"
end
return table.concat(pts, "\n")
end
end
return ''
end
local function uni_test2(frame)
local text = frame.args[1]
local search = frame.args['search']
local replace = frame.args['replace']
if isset(text) and isset(search) and isset(replace) then
return string.utf8replace(text, { {search, replace} })
else
return uni_test(frame)
end
end
function lookup_song_info(frame)
lst = load_info(frame.args['src'])
local k1 = normalize(frame.args['game'])
local k2 = normalize(frame.args['name'])
if isset(lst[k1]) and isset(lst[k1][k2]) then
local trimmed_info = lst[k1][k2]
local s,e,title,info = string.find(trimmed_info, '^(.*)%s*%[(.+)%]')
if s ~= nil and isset(info) then
local expinfo = string.explode(info, ';')
local charname = expinfo[#expinfo] -- last element of table
if mw.exists(charname) then
expinfo[#expinfo] = '[\[' .. charname .. ']]'
end
trimmed_info = title .. ' [' .. table.concat(expinfo, "; ") .. ']'
end
return trimmed_info
end
return '' -- so we won't get a 'nil' as result if can't find a match
end
return {
['outputLyrics'] = outputLyrics,
['outputLyricsFromTemplate'] = function(frame) return outputLyrics(frame:getParent()) end,
['dump_info'] = dump_info,
['unitest'] = uni_test,
['unitest2'] = uni_test2,
['lookup_song_info'] = lookup_song_info,
}
-- [[Category:Lua Scripts|{{PAGENAME}}]]