跳转到内容

模組:TalkIcon

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书

local mYesno
local data = require('Module:TalkIcon/data')

local p = {}

local function killEmptyString(value)
	return value ~= '' and value or nil
end

local function yesno(value)
	if not mYesno then
		mYesno = require('Module:Yesno')
	end
	return mYesno(value)
end

local wrapToTable = {
	quote = { '「', '」' },
	book = { '《', '》' },
	['single-book'] = { '〈', '〉' },
	-- [[Wikipedia:格式手册/标点符号#书名号]]
	volume = { '-{zh-cn:《;zh-my:《;zh-sg:《;zh-hk:《;zh-mo:《;zh-tw:〈;}-', '-{zh-cn:》;zh-my:》;zh-sg:》;zh-hk:》;zh-mo:》; zh-tw:〉;}-' }, -- [[Template:單雙書名號轉換]]
	series = { '-{zh-cn:“; zh-tw:《;}-', '-{zh-cn:”; zh-tw:》;}-' }, -- [[Template:引书号转换]]
}
local wrapToAliasTable = {
	q = wrapToTable.quote,
	['引号'] = wrapToTable.quote,
	['引號'] = wrapToTable.quote,
	b = wrapToTable.book,
	['书名号'] = wrapToTable.book,
	['書名號'] = wrapToTable.book,
	['单书名号'] = wrapToTable['single-book'],
	['單書名號'] = wrapToTable['single-book'],
	['double-book'] = wrapToTable.book,
	['双书名号'] = wrapToTable.book,
	['雙書名號'] = wrapToTable.book,
	v = wrapToTable.volume,
	s = wrapToTable.series,
	-- 代號參見[[Wikipedia:格式手册/标点符号#书名号]]
	['b1'] = wrapToTable.book,
	['书1'] = wrapToTable.book,
	['書1'] = wrapToTable.book,
	['b2'] = wrapToTable.volume,
	['书2'] = wrapToTable.volume,
	['書2'] = wrapToTable.volume,
	['b3'] = wrapToTable.series,
	['书3'] = wrapToTable.series,
	['書3'] = wrapToTable.series,
}

local function wrapTo(to, opt)
	if opt then
		opt = mw.ustring.lower(mw.text.trim(opt))
		local match = wrapToTable[opt] or wrapToAliasTable[opt]
		if match then
			to = match[1] .. to .. match[2]
		end
	end
	return to
end

p._wrapTo = wrapTo -- debug only

local function isFile(input, allowNoNs)
	local success, title = pcall(mw.title.new, input)
	if success and title then
		if title.namespace == 0 and allowNoNs then
			title = mw.title.new(title.text, 6)
		end
		if title.namespace == 6 then
			return title
		end
	end
	return false
end

p._isFile = isFile -- debug only

local function detectMultipleFiles(input)
	if input:sub(1, 6):lower() ~= 'multi:' then
		return {}
	end
	local rawFiles = mw.text.split(input:sub(7), '/') -- 理論上檔名不能包含 / 所以應該安全?
	local files = {}
	for _, name in ipairs(rawFiles) do
		local title = isFile(name, true)
		if title then
			table.insert(files, title)
		end
	end
	return files
end

p._detectMultipleFiles = detectMultipleFiles -- debug only

local function getTypeClassList(iconTag, type)
	if not type or mw.text.trim(type) == '' then
		return
	end

	for _, current in ipairs(mw.text.split(type, ',')) do
		current = mw.text.trim(current)
		local sp = mw.text.split(current, '-')
		if #sp <= 2 then
			if data.type[sp[1]] then
				local t = data.type[sp[1]]
				local typeClass = string.format('zhwiki-%sicon', t.type)
				iconTag:addClass(typeClass)
				if killEmptyString(sp[2]) and t.sub[sp[2]] then
					iconTag:addClass(string.format('%s-%s', typeClass, t.sub[sp[2]]))
				end
			end
		end
	end
end

p._getTypeClassList = getTypeClassList -- debug only

function p._main(args)
	local icon = killEmptyString(args.icon or args['1'] or args[1]) -- 圖標
	if not icon then
		error('missing argument "icon".')
	end

	local out
	if killEmptyString(args.wrapClass) or killEmptyString(args.wrapStyles) then
		out = mw.html.create('span')
			:addClass(killEmptyString(args.wrapClass)) -- # nullable
			:cssText(killEmptyString(args.wrapStyles)) -- # nullable
	else
		out = mw.html.create()
	end

	local iconTag = out:tag('span')
		:addClass('skin-invert zhwiki-talkicon')

	if killEmptyString(args.type) then
		getTypeClassList(iconTag, args.type)
	end

	if killEmptyString(args.id) then
		iconTag:addClass('zhwiki-talkicon-' .. args.id:gsub(' ', '_'))
	end
	iconTag
		:addClass(killEmptyString(args.iconClass)) -- # nullable
		:attr('title', killEmptyString(args.iconTitle)) -- # nullable
		:attr('lang', killEmptyString(args.iconLang)) -- # nullable

	local iconFiles = detectMultipleFiles(icon)
	local iconFile
	if #iconFiles > 0 then
		local tmp = {}
		iconFile = true
		for _, file in ipairs(iconFiles) do
			-- 多圖模式不支援自定義連結和替代文字
			table.insert(tmp, string.format(
				'[[File:%s|%s|link=|alt=]]',
				file.text,
				args.iconSize or args.size or '20px'
			))
		end
		icon = table.concat(tmp, '')
	else
		iconFile = isFile(icon)
		if iconFile then
			-- 傳入圖片檔名
			icon = string.format(
				'[[File:%s|%s|link=%s|alt=%s]]',
				iconFile.text,
				args.iconSize or args.size or '20px',
				args.iconLink or args.link or '',
				args.iconAlt or args.alt or ''
			)
		else
			-- 傳入一般內容
			iconTag
				:css('font-weight', 'bold')
				:css('color', killEmptyString(args.iconColor or args.fg)) -- # nullable
				:css('background', killEmptyString(args.iconBackground or args.bg)) -- # nullable
		end
	end
	iconTag
		:cssText(killEmptyString(args.iconStyles)) -- # nullable
		:wikitext(icon)

	local text = args.text or args['2'] or args[2] -- 圖標文字
	if text then
		local iconSpace
		if killEmptyString(args.iconSpace) then
			iconSpace = yesno(args.iconSpace)
		else
			iconSpace = iconFile and true or false
		end
		if iconSpace then
			out:wikitext('&nbsp;') -- 圖標和文字中間插一個空格
		end

		local textTag = out:tag('span')
			:addClass('zhwiki-talkicontext')
			:addClass(killEmptyString(args.textClass)) -- # nullable
			:css('font-weight', 'bold')
			:cssText(killEmptyString(args.textStyles)) -- # nullable
			:wikitext(text)

		local extra = killEmptyString(args.extra or args['3'] or args[3])
		if extra then
			-- 如 (►)'''移動'''到某某標題
			out:wikitext(extra)
		elseif killEmptyString(args.explain) then
			-- 解釋 大多用於 VIP、RFPP、SPI 之類的模板
			-- 其實等同於 |extra=:<explain>
			-- 但作者認為在參數開頭就莫名加冒號很醜 僅此而已 (2025.08.13)
			out:wikitext(':' .. args.explain)
		elseif killEmptyString(args.to) then
			-- (動作)至XXX
			-- 等同於 |extra=至{{#if:{{{to|}}}|{{#switch:{{{wrapTo}}}|q|quote=「|b|book=《|v|volume=(...略)|s|series=(...略)}}[[{{{to}}}]{{#switch:{{{wrap}}}|q|quote=」|b|book=》|v|volume=(...略)|s|series=(...略)}}]}}
			-- 模板請務必傳遞 wrapTo 參數以因應需求 避免重複建立大量模板
			out:wikitext('至' .. wrapTo(string.format('[[%s]]', args.to), args.wrapTo))
		end
	end

	return tostring(out)
end

function p.main(frame)
	local args = frame.args['passThrough'] and ((frame:getParent() or frame).args) or frame.args
	return p._main(args)
end

return p