2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

Module:VersionHistory

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索
Template-info.svg 模块文档  [创建] [刷新]
local module = {}

local getArgs = require('Module:Arguments').getArgs
function module.funcSelector(frame)
	local args = getArgs(frame)
	local funcName = args["_funcName"]
	if funcName == "main" then
		return module.main(args)
	elseif funcName == "entry" then
		return module.entry(args)
	end
end

function module.main(args)
	local doc = {}
	if args["docname"] == nil then
		for i, line in ipairs(args) do
			for _i, entry in ipairs(module.parseEntries(line)) do
				table.insert(doc, entry)
			end
		end
	else
		local docname = args["docname"]
		local v1, v2 = mw.ustring.find(docname, ":")
		local title = mw.title.makeTitle(mw.ustring.sub(docname, 1, v1 - 1), mw.ustring.sub(docname, v2 + 1, mw.ustring.len(docname)))
		local doccontent = title:getContent() --读取JSON格式的版本历史元数据页的所有内容。
		doc = mw.text.jsonDecode(doccontent)
	end
	
	for i, entry in ipairs(doc) do module.normalizeEntry(entry) end --标准化版本历史项。
	return module.output(doc, args)
end

function module.entry(args)
	local entry = {}
	if args["version"] ~= nil then entry.version = args["version"] end
	if args["time"] ~= nil then entry.time = args["time"] end
	if args["summary"] ~= nil then
		entry.summary = mw.ustring.gsub(args["summary"], ";", "//;//")
		
		if args["details"] ~= nil then
			entry.details = mw.ustring.gsub(args["details"], ";", "//;//")
		end
	end
	
	module.normalizeEntry(entry)
	local output = args["output"]
	if output == "json" then
		return mw.text.jsonEncode(entry)
	else
		return mw.ustring.format("version:%s;time:%s;summary:%s;details:%s", entry.version, entry.time, entry.summary, entry.details)
	end
end

function module.output(doc, args)
	table.sort(doc,
		function(e1, e2)
			if e1.version ~= nil and e2.version ~= nil then return e1.version < e2.version
			elseif e1.time ~= nil and e2.time ~= nil then return e1.time < e2.time
			else return true
			end
		end
	)
	
	local output = args["output"]
	if output == "json" then
		return mw.text.jsonEncode(doc)
	else
		local rLines = {}
		for i, entry in ipairs(doc) do
			local frame = mw.getCurrentFrame()
			
			local iif = function(value, valueWhenNil)
				if value == nil then return valueWhenNil
				else return value
				end
			end
			local savefunc = function(condition, func)
				if condition == nil then return nil
				else return func(condition)
				end
			end
			
			local fullurl = savefunc(entry.version, function()
				return frame:callParserFunction("fullurl", { args["pagename"], "oldid="..entry.version })
			end)
			local timeStr = iif(savefunc(entry.time, function()
				return frame:callParserFunction("#time", { "Y年m月d日 (D) H:i", entry.time })
			end), "<时间数据丢失>")
			local linkStr
			if fullurl == nil then
				linkStr = timeStr
			else
				linkStr = mw.ustring.format("[%s %s]", fullurl, timeStr)
			end
			
			local descriptionStr
			if entry.summary == nil then
				descriptionStr = ""
			else
				local span_summary = mw.html.create("span")
				span_summary:wikitext(frame:preprocess(entry.summary))
				if entry.details == nil then
					descriptionStr = tostring(span_summary)
				else
					span_summary:attr("class", toggleid)
								:attr("style", "cursor:pointer")
								:attr("title", args["pagename"])
					local div_details = mw.html.create("div")
					div_details:attr("class", "mw-collapsible mw-collapsed")
							   :attr("id", toggleid)
							   :wikitext(frame:preprocess(entry.details))
					local widgetInvoke = frame:callParserFunction("#Widget", { "Toggle" })
					
					descriptionStr = mw.ustring.format("%s%s%s", tostring(span_summary), tostring(div_details), widgetInvoke)
				end
			end
			
			table.insert(rLines, mw.ustring.format("*%s %s", linkStr, descriptionStr))
		end
		return table.concat(rLines, "\r\n")
	end
end

function module.parseEntries(str)
	str = mw.text.unstripNoWiki(str)
	str = mw.text.decode(str)
	
	local entryStrs = module.splitWithEscape(str, "///;\\\\\\", "//////;\\\\\\\\\\\\")
	
	local entries = {}
	for i, entryStr in ipairs(entryStrs) do
		if mw.text.trim(entryStr) ~= "" then
			local version, time, summary, details
			local splitResult = module.splitWithEscape(entryStr, ";", "//;//")
			
			for _i, v in ipairs(splitResult) do
				local v1, v2 = mw.ustring.find(v, ":", nil, true)
				if v1 ~= nil then
					local key = mw.ustring.sub(v, 1, v1 - 1)
					local value = mw.ustring.sub(v, v2 + 1, mw.ustring.len(v))
					if key == "version" then
						version = value
					elseif key == "time" then
						time = value
					elseif key == "summary" then
						summary = value
					elseif key == "details" then
						details = value
					end
				end
			end
			
			if version ~= nil or time ~= nil or summary ~= nil or details ~= nil then
				table.insert(entries, { version = version, time = time, summary = summary, details = details })
			end
		end
	end
	
	return entries
end

function module.splitWithEscape(str, separator, escape)
	if str == nil then error("str的值为nil。") end
	if separator == nil then error("separator的值为nil。") end
	if escape == nil then error("escape的值为nil。") end
	
	local v1, v2 = mw.ustring.find(escape, separator, 1, true)
	if v1 == nil then error("separator应为escape的一部分") end
	
	local preLen = v1 - 1
	local sufLen = mw.ustring.len(escape) - v2
	
	local result = {}
	local p_start = 1
	local p_buffer = p_start
	local length = mw.ustring.len(str)
	while p_start < length do
		v1, v2 = mw.ustring.find(str, separator, p_buffer, true)
		if v1 == nil then break --不再有分隔符
		elseif (v1 < p_buffer + preLen or v2 > length - sufLen) or mw.ustring.sub(str, v1 - preLen, v2 + sufLen) ~= escape then --不是转义序列
			--分割字符串
			temp_escapedStr = mw.ustring.gsub(mw.ustring.sub(str, p_start, v1 - 1), escape, separator)
			table.insert(result, temp_escapedStr)
			
			--更新起始指针和缓存指针
			p_start = v2 + 1
			p_buffer = p_start
		else --是转义序列
			--更新缓存指针
			p_buffer = v2 + sufLen + 1
		end
	end
	temp_escapedStr = mw.ustring.gsub(mw.ustring.sub(str, p_start, length), escape, separator)
	table.insert(result, temp_escapedStr)
	
	return result
end

function module.normalizeEntry(entry)
	if entry == nil then error("entry的值为nil。") end
	
	if entry.version ~= nil then
		entry.version = mw.ustring.match(tostring(entry.version), "%d+")
	end
	if entry.time ~= nil then
		entry.time = module.time2timeStr(module.timeStr2time(entry.time))
	end
	if entry.summary == nil and entry.details ~= nil then
		entry.details = nil
	end
end

function module.timeStr2time(timeStr)
	if timeStr == nil then error("timeStr为空") end
	if type(timeStr) ~= "string" then error("timeStr的类型不是函数接受的参数类型") end
	
	local year, month, day, hour, min
	if year == nil then year, month, day, hour, min = mw.ustring.match(timeStr, "(%d+)年(%d+)月(%d+)日 %([日一二三四五六]%) (%d+):(%d+)") end
	if year == nil then year, month, day, hour, min = mw.ustring.match(timeStr, "(%d+)-(%d+)-(%d+)T(%d+):(%d+):%d+Z") end
	if year == nil then year, month, day, hour, min = mw.ustring.match(timeStr, "(%d+)-(%d+)-(%d+) (%d+):(%d+)") end
	
	if year == nil then error("time格式不正确。") end
	
	if year == nil then return nil
	else return { year = tonumber(year), month = tonumber(month), day = tonumber(day), hour = tonumber(hour), min = tonumber(min) }
	end
end

function module.time2timeStr(time)
	if time == nil then error("time为空") end
	if type(time) ~= "table" then error("time的类型不是函数接受的参数类型") end
	
	return os.date("%Y-%m-%d %H:%M", os.time(time))
end

return module