Module:Lyrics/colors

From Moegirlpedia
< Module:Lyrics
Revision as of 21:45, 26 March 2022 by LiaMinina (talk | contribs) (Created page with "local module = {} local getArgs = require('Module:Arguments').getArgs local rainbow = require('Module:rainbow')._main local genCharaBlock = require('Module:Lyrics/colors/sub'...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search
Template-info.svg Module Documentation  [View] [Edit] [History] [Refresh]

This module provides an easy way to create large sections of lyrics with translations, using multiple colors in the lyrics to distinguish between different singers.

local module = {}

local getArgs = require('Module:Arguments').getArgs
local rainbow = require('Module:rainbow')._main
local genCharaBlock = require('Module:Lyrics/colors/sub')._charaBlock
local buildLyrics = require('Module:Lyrics')._lyrics
local initHandler = require('Module:HooksHandler').init

function module.parseArgs(colorsStr, charasStr, charaBlock)
	local colors = mw.text.split(mw.ustring.gsub(colorsStr, ';+$', ''), '%s*;+%s*')
	local aliases = {}
	for idx, val in ipairs(colors) do
		if mw.ustring.find(val, '=', 1, true) then
			colors[idx], aliases[idx] = mw.ustring.match(val, '^%s*([^=]+)%f[%s=]%s*=%s*(.+)%f[%s%z]%s*$')
		end
	end

	local charas = mw.text.split(mw.ustring.gsub(charasStr, ';+$', ''), '%s*;+%s*')
	for idx, val in ipairs(charas) do
		if charaBlock then
			charas[idx] = mw.ustring.gsub(val, '%(.+%)', '')
		end
	end

	local map_aliases = {}
	for idx, val in pairs(aliases) do
		map_aliases[val] = idx
	end
	for idx, val in pairs(charas) do
		map_aliases[val] = idx
	end

	return colors, map_aliases, charas
end

function module.parse(colors, charas, chorusName, sentence)
	local count_colors = #colors
	-- 包裹节点
	local chorus = '合唱'
	if #charas > count_colors then chorus = charas[#charas] end
	if chorusName ~='' then chorus = chorusName end
	for i=1, #sentence do
		local preParsed = mw.ustring.gsub(sentence[i], '(@%d%d?)([^@\n]+)\n-', function(m1, m2)
			if m1:len() > 2 and tonumber(m1:sub(2)) > count_colors then
				m1 = m1:sub(1, 2)
				m2 = m1:sub(3) .. m2
			end
			return mw.ustring.format('<*span title="%s" style="color:%s">%s<*/span>', m1, m1, m2)
		end)
		if mw.ustring.find(sentence[i], '^@d+.-@.') or mw.ustring.find(sentence[i], '^[^@]') then
			preParsed = '<span class="NoTitlebox" title="'..chorus..'">'..preParsed..'</span>'
		end
		sentence[i] = preParsed
	end

	local mode = {
		lg = 'linear',
		rg = 'radial',
		rlg = 'repeating-linear',
		rrg = 'repeating-radial',
		rb = 'linear'
	}
	local special_color = {}
	for idx, val in ipairs(colors) do
		local match = val:match('^([lrc][lr]?[gbo])%(', 1)
		if match and (mode[match] or match == 'co') then
			special_color[idx] = match
		end
	end
	for i, material in ipairs(sentence) do
		local need_special = {}
		material = mw.ustring.gsub(material, 'color:@(%d+)', function (m)
			m = tonumber(m)
			if special_color[m] then need_special[special_color[m]] = true end
			return 'color:' .. (colors[m] or '')
		end)
		material = mw.ustring.gsub(material, 'title="@(%d+)"', function (m)
			m = tonumber(m)
			return charas[m] and ('title="' .. charas[m] ..'"') or ''
		end)

		-- 渐变色实现
		for k, _ in pairs(need_special) do
			v = mode[k]
			if v then
				material = mw.ustring.gsub(material, '<%*span ([^>]-)style="color:'..k..'(%(.-%))">(.-)<%*/span>', function(s1, s2, s3)
					if k == 'rb' then
						local pattern = ''
						s2 = mw.ustring.gsub(s2, '%((.+)%)', '%1')
						local colors = mw.text.split(s2, ',', true)
						local quotient = 100 / #colors
						for i, v in ipairs(colors) do
							local color = mw.text.trim(v)
							if i == 1 then
								pattern = color..' '..quotient..'%'
							else
								pattern = pattern..', '..color..' '..(quotient * (i - 1))..'%'..', '..color..' '..(quotient * i)..'%'
							end
						end
						s2 = '('..pattern..')'
					end
					local style = 'background:-webkit-'..v..'-gradient'..s2..';-webkit-background-clip:text;-webkit-text-fill-color:transparent;-webkit-box-decoration-break:clone;'
					--[[
					s3 = mw.ustring.gsub(s3, '<rt%s*([^>]-)%s*>', function(s)
						if s ~= '' then
							local before, attr, after = mw.ustring.match(s, '^(.-style=")([^"]*)(".*)$')
							if attr ~= nil and mw.text.trim(attr) == '' then attr = nil end
							if attr ~= nil then
								s = before..attr..'; '..style..after
							else
								s = (before or s..' style="')..style..(after or '"')
							end
						else
							s = ' style="'..style..'"'
						end
						return '<rt '..s..'>'
					end)
					]]--
					-- 额外套一层span,以免影响titlebox
					return '<span '..s1..'><span class="Lyrics-gradient" style="'..style..'">'..s3..'</span></span>'
				end)
			end
		end

		-- 交替色实现
		if need_special['co'] then
			material = mw.ustring.gsub(material, '<%*span ([^>]-)style="color:co%((.-)%)">(.-)<%*/span>', 
			function(s1, s2, s3)
				local colors = mw.text.split(s2, ',')
				for idx, val in ipairs(colors) do
					colors[idx] = mw.text.trim(val)
				end	
				return '<span '..s1..'>'..rainbow(colors, s3)..'</span>'
			end)
		end

		-- 整理节点
		sentence[i] = mw.ustring.gsub(mw.ustring.gsub(material, '<%*(/?span)', '<%1'), '@', '')
	end

	return sentence
end

local preSplit = function (original, translated, customArgs)
	local ce = {}
	local deal = function (text)
		text = mw.ustring.gsub(text, '%$([%d%$%[@])', function(s)
			if not ce[s] then ce[s] = mw.getCurrentFrame():expandTemplate{ title = 'ce', args = {s} } end
			return ce[s]
		end)
		text = mw.ustring.gsub(text, '@%[([^%[]-)%]', function(match) return '@' .. (customArgs.map_aliases[match] and customArgs.map_aliases[match] or '['..match..']') end)
		return text
	end
	return deal(original), deal(translated)
end

local preParse = function (original, translated, customArgs)
	original = module.parse(customArgs.colors, customArgs.charas, customArgs.chorusName, original)
	if customArgs.traColors and translated then
		translated = module.parse(customArgs.colors, customArgs.charas, customArgs.chorusName, translated)
	end
end

local preOutput = function (html, customArgs)
	if customArgs.doCharaBlock then
		html =  '<p>' .. genCharaBlock(customArgs.genCharaBlock) .. '</p>' .. html
	end
	local css = mw.getCurrentFrame():extensionTag{ name = 'templatestyles', args = { src = 'Template:LyricsKai/colors/styles.css' } }
	return css .. html
end

function module.initHooks(args, hooksHandler, customArgs)
	customArgs.traColors = (args.traColors or '') == 'on'
	customArgs.doCharaBlock = (args.charaBlock or '') == 'on'
	customArgs.chorusName = args.chorusName or ''
	customArgs.colors, customArgs.map_aliases, customArgs.charas = module.parseArgs(args.colors or '', args.charas or '', customArgs.doCharaBlock)
	customArgs.genCharaBlock = { colors = args.colors or '', charas = args.charas or '', groupName = args.groupName, groupColor = args.lstyle }

	return hooksHandler({ preSplit = preSplit, preParse = preParse, preOutput = preOutput })
end

function module.main(frame)
	local args = getArgs(frame, { wrappers = 'Template:LyricsKai/colors' })
	local hooksHandler, customArgs = initHandler(), {}
	local hookTrigger = module.initHooks(args, hooksHandler, customArgs)

	return buildLyrics(args, hookTrigger, customArgs)
end

return module