跳转到内容
打开/关闭菜单
打开/关闭外观设置菜单
打开/关闭个人菜单
未登录
登录后可编辑和发表评论。

Module:Infobox/new

来自Vocawiki

用于{{Infobox/new}}

local p = {}

-- 参数别名
local ALIASES = {
	title          = { "标题", "標題" },
	image          = { "图片", "圖片" },
	size           = { "图片大小", "圖片大小" },
	caption        = { "图片说明", "圖片說明", "图片信息", "圖片信息" },
	tabs           = {},
	float          = {},
	class          = {},
	order          = {},
	["title-bg"]   = { "标题背景色", "標題背景色" },
	["title-color"]  = { "标题文字色", "標題文字色" },
	["header-bg"]    = { "横栏背景色", "橫欄背景色" },
	["header-color"] = { "横栏文字色", "橫欄文字色" },
	["label-bg"]     = { "标签背景色", "標籤背景色" },
	["label-color"]  = { "标签文字色", "標籤文字色" },
	collapsible    = {},
	collapsed      = {},
	["data-align"]  = { "数据对齐", "資料對齊" },
}

local CANON = {}
for std, aliases in pairs(ALIASES) do
	CANON[std] = std
	for _, a in ipairs(aliases) do CANON[a] = std end
end

local DEFAULTS = { size = "300px", float = "right" }

-- trim,空串返回nil
local function trim(s)
	if type(s) ~= "string" then return nil end
	s = mw.text.trim(s)
	return s ~= "" and s or nil
end

local function is_present(s)
	return type(s) == "string" and mw.text.trim(s) ~= ""
end

-- 准备文本
local function prep_wikitext(text)
	if not text or text == "" then return text end
	if text:match("^[*#;:%{|!]") then text = "\n" .. text end
	text = text:gsub("\n([^*#;:%{|!])", " %1")
	return text
end

-- 解析order
local function parse_order(s)
	local list = {}
	if not s then return list end
	for item in mw.text.gsplit(s, "%s*,%s*") do
		item = mw.text.trim(item)
		if item ~= "" then
			local c = mw.ustring.sub(item, 1, 1)
			if c == "-" then
				list[#list + 1] = { kind = "header", key = mw.ustring.sub(item, 2) }
			elseif c == "_" then
				list[#list + 1] = { kind = "free",   key = mw.ustring.sub(item, 2) }
			else
				list[#list + 1] = { kind = "data",   key = item }
			end
		end
	end
	return list
end

-- header后是否还有可见内容
local function has_content_after(fields, start_idx, args)
	for i = start_idx + 1, #fields do
		local f = fields[i]
		if f.kind == "data" then
			if is_present(args[f.key]) then return true end
		elseif f.kind == "free" then
			return true
		end
	end
	return false
end

local function header_text(f, args)
	return trim(args["-" .. f.key]) or f.key
end

local function free_text(f, args)
	return trim(args["_" .. f.key]) or f.key
end

-- 构建内联CSS
local function css(map)
	local parts = {}
	for k, v in pairs(map) do
		if v then parts[#parts + 1] = k .. ":" .. v end
	end
	return next(parts) and table.concat(parts, ";") or nil
end

-- 解析折叠节号
local function parse_collapsed(raw)
	local set = {}
	if raw then
		for n in mw.text.gsplit(raw, "%s*,%s*") do
			n = tonumber(mw.text.trim(n))
			if n then set[n] = true end
		end
	end
	return set
end

-- 构建信息栏
local function build(args)
	local float = trim(args["float"]) or DEFAULTS.float
	local extra = trim(args["class"])

	local root = mw.html.create("div")
		:addClass("infobox")
		:addClass("infobox-float-" .. float)
	if extra then root:addClass(extra) end
	local data_align = trim(args["data-align"])
	if data_align then root:addClass("infobox-data-" .. data_align) end

	-- 主表格
	local main = root:tag("table"):addClass("infobox-main")

	local title = trim(args["title"])
	if title then
		local s = css { background = trim(args["title-bg"]), color = trim(args["title-color"]) }
		main:tag("tr"):addClass("infobox-title")
			:tag("th"):attr("colspan", "2")
			:cssText(s)
			:wikitext(title)
	end

	local tabs_val = trim(args["tabs"])
	local img_val  = trim(args["image"])
	if tabs_val or img_val then
		local td = main:tag("tr"):addClass("infobox-image")
			:tag("td"):attr("colspan", "2")
		if tabs_val then
			td:wikitext(tabs_val)
		else
			td:wikitext(string.format("[[File:%s|%s|class=infobox-img]]",
				img_val, trim(args["size"]) or DEFAULTS.size))
		end
	end

	local cap = trim(args["caption"])
	if cap then
		main:tag("tr"):addClass("infobox-caption")
			:tag("td"):attr("colspan", "2")
			:wikitext(cap)
	end

	-- 数据区域
	local fields = parse_order(trim(args["order"]))
	if #fields == 0 then return tostring(root) end

	local collapsible = trim(args["collapsible"]) ~= "0" and trim(args["collapsible"]) ~= "no"
	local collapsed_set = parse_collapsed(trim(args["collapsed"]))
	local header_css = css { background = trim(args["header-bg"]), color = trim(args["header-color"]) }
	local label_css  = css { background = trim(args["label-bg"]),   color = trim(args["label-color"]) }

	local section_id = 0
	local cur_tbl = nil

	local function flush()
		if cur_tbl then root:node(cur_tbl); cur_tbl = nil end
	end

	local function new_data_table(can_collapse)
		local tbl = mw.html.create("table"):addClass("infobox-section")
		if can_collapse then
			tbl:addClass("mw-collapsible")
			if collapsed_set[section_id] then tbl:addClass("mw-collapsed") end
			tbl:attr("id", "mw-customcollapsible-" .. section_id)
		end
		return tbl
	end

	for i, f in ipairs(fields) do
		if f.kind == "header" then
			if has_content_after(fields, i, args) then
				flush()
				section_id = section_id + 1

				local hdr_tbl = mw.html.create("table"):addClass("infobox-section")
				local hdr_tr  = hdr_tbl:tag("tr"):addClass("infobox-header")
				if collapsible then hdr_tr:addClass("mw-customtoggle-" .. section_id) end

				hdr_tr:tag("th"):attr("colspan", "2")
					:cssText(header_css)
					:wikitext(prep_wikitext(header_text(f, args)))
					:tag("span"):addClass("infobox-collapse-icon")

				root:node(hdr_tbl)
				cur_tbl = new_data_table(collapsible)
			end

		elseif f.kind == "free" then
			if not cur_tbl then cur_tbl = new_data_table(false) end
			cur_tbl:tag("tr"):addClass("infobox-full")
				:tag("td"):attr("colspan", "2")
				:wikitext(prep_wikitext(free_text(f, args)))

		else -- data
			local val = trim(args[f.key])
			if val then
				if not cur_tbl then cur_tbl = new_data_table(false) end
				local row = cur_tbl:tag("tr"):addClass("infobox-row")
				row:tag("td"):addClass("infobox-label")
					:cssText(label_css)
					:wikitext(f.key)
				row:tag("td"):addClass("infobox-data")
					:wikitext(prep_wikitext(val))
			end
		end
	end

	flush()
	return tostring(root)
end

-- 合并参数并解析别名
local function merge_args(frame)
	local args = {}
	local parent = frame:getParent()
	if parent then
		for k, v in pairs(parent.args) do
			local val, raw = trim(v), trim(tostring(k))
			if val and raw then
				local m = CANON[mw.ustring.lower(raw)]
				args[m or raw] = val
			end
		end
	end
	for k, v in pairs(frame.args) do
		local val, raw = trim(v), trim(tostring(k))
		if val and raw then
			local m = CANON[mw.ustring.lower(raw)]
			args[m or raw] = val
		end
	end
	return args
end

-- 入口:显式order
function p.render(frame)
	return build(merge_args(frame))
end

-- 入口:自动模式
function p.main(frame)
	local args = merge_args(frame)
	if not trim(args["order"]) then
		local allkeys = trim(frame.args["__allkeys"])
		if allkeys then
			local order = {}
			for key in mw.text.gsplit(allkeys, "%s*,%s*") do
				local k = trim(key)
				if k and not CANON[mw.ustring.lower(k)] then
					order[#order + 1] = k
				end
			end
			args["order"] = table.concat(order, ", ")
		end
	end
	return build(args)
end

-- 返回所有控制参数名
function p.controlParams(frame)
	local list = {}
	for std, aliases in pairs(ALIASES) do
		list[#list + 1] = std
		for _, a in ipairs(aliases) do list[#list + 1] = a end
	end
	return table.concat(list, "|")
end

return p