置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:扩展时间线

贴贴♀百科,万娘皆可贴的百科全书!转载请标注来源页面的网页链接,并声明引自贴贴百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [创建] [刷新]
local module = {}
 
local getArgs = require('Module:Arguments').getArgs
local timeLineModule=require('Module:时间线')
local timeLine=timeLineModule.timeLine
local preArgs=timeLineModule.preArgs

local clone=function(self)--浅表克隆
	local new=setmetatable({},getmetatable(self))
	for k,v in pairs(self) do
		new[k]=v
	end
	return new
end
local indexOf=function(list,item)--获得序号
	for i=1,#list do
		if list[i]==item then
			return i
		end
	end	
end
local bit32 = require( 'bit32' )
function bit32.get(bits,index)--设置index处的真值
	return bit32.band(bits,bit32.lshift(1, index-1))~=0
end
local textPredicate = require( 'Module:TextPredicate' )

--标记功能
local signFunction={
	symbolSplit="#",
	collapsableDefault=true
}

function signFunction:_getNameList(text)
	local names=mw.text.split(text,self.symbolSplit)
	local values={}
	for index,name in ipairs(names) do
		if name:sub(1,1)=="-" then
			values[name]=false
			names[index]=name:sub(2)
		else
			values[name]=true
		end
	end
	return names,values
end
function signFunction:_getPattern(text)
	local tab={}
	for text in mw.text.gsplit(text,self.symbolSplit) do
		local b,e=text:find(":")
		assert(b,"auto参数不合法")
		local name=text:sub(1,b-1)
		if not tab[name] then
			tab[name]={text:sub(e+1)}
		else
			table.insert(tab[name],text:sub(e+1))
		end
	end
	return tab
end
function signFunction:init(tab,args)
	self.__index = self
	local result=setmetatable(tab or {},self)
	
	if result.var then
		signFunction.var_array=signFunction.var_array or require('Module:Var-array')
		result.var=result.var=="all" and {} or mw.text.split(result.var,result.symbolSplit)
		result.preLoad=signFunction.preLoad_var
		result.preFormat=signFunction.preFormat_var
	end
	if result.collapsable then
		if result.collapsable=="all" then
			self.collapsableDefault=true
			result.collapsableValues={}
		elseif result.collapsable=="allnot" then
			self.collapsableDefault=false
			result.collapsableValues={}
		else
			result.collapsable,result.collapsableValues=result:_getNameList(result.collapsable)
		end
		result.formatNode=signFunction.formatNode_collapsible
	end
	if result.auto then
		result.auto=result:_getPattern(result.auto)
		result.analysisText=signFunction.analysisText_auto
	end
	return result
end
function signFunction:preLoad_var()
	self.tabs={}
	self.signs=signFunction.var_array.get("module.timeLine.ex.signs") or {}
	self.predicates={}
	for index,valueText in ipairs(self.signs) do
		local predicate=textPredicate.createText(valueText)
		if #self.var==0 or not predicate:limitTo(self.var):isReal() then 
			self.predicates[index]=predicate
			self.tabs[index]={
				texts		=signFunction.var_array.get("module.timeLine.ex.texts."..valueText),
				times		=signFunction.var_array.get("module.timeLine.ex.times."..valueText),
			}
			--mw.log("load",valueText,#self.tabs[index].texts,#self.tabs[index].times)
		else
			self.predicates[index]=false
		end
	end
	return self.tabs
end
function signFunction:preLoad()
	if self.var then
		self:preLoad_var()
	end
end

function signFunction.preArgs_step(self,state)--time,text,sign
	state.index=state.index+1
	if state.mIndex then
		if state.index>#state.tab.texts then
			state.mIndex,state.tab=next(self.tabs,state.mIndex)
			if not state.mIndex then
				return nil
			end
			state.predicate=self.predicates[state.mIndex]
			--mw.log("state",state.mIndex,#state.tab)
			state.index=1
		end
	else
		return nil
	end
	return state,state.tab.times[state.index],state.tab.texts[state.index],state.predicate
end
function signFunction:preArgs()--读取输入组
	local state={ index=0 }
	state.mIndex,state.tab=next(self.tabs)
	state.predicate=self.predicates[state.mIndex]
	--mw.log("state",state.mIndex,state.mIndex and self.predicates[state.mIndex],state.tab and #state.tab.texts)
	return signFunction.preArgs_step,self,state
end

function signFunction:analysisText_base(originText)
	if not originText or originText=="" then return nil,nil end

	local b,e=originText:find(self.symbolBegin)--作为开端
	if not b then
		return nil,originText
	end
	result=textPredicate.createExpress(originText:sub(e+1))
	local other=originText:sub(1,b-1)
	return result,other
end
function signFunction:analysisText(originText)
	local result,other=self:analysisText_base(originText)
	return result or textPredicate._true,other
end
function signFunction:analysisText_auto(originText)
	local result,other=signFunction.analysisText_base(self,originText)
	if not other then
		return result or textPredicate._true,other
	end
	result=result or textPredicate.base()
	for name,tab in pairs(self.auto) do
		for _,pattern in ipairs(tab) do
			if other and other:find(pattern) then
				result:addExpress(name)
			end
		end
	end
	if result:isReal(false) then
		result=textPredicate._true
	end
	--mw.log(result)
	return result,other
end
function signFunction:formatNode(node,predicate)
	return node
end
function signFunction:formatNode_collapsible(nodeIn,predicate)
	--mw.log(predicate,nodeIn)
	if #self.collapsable>0 then
		predicate=predicate:limitTo(self.collapsable)
		--mw.log("--",predicate)
		if predicate:isReal() then
			return nodeIn
		end--elseif predicate:isReal(false) then
	end
	
	local nodeMain= mw.html.create()
	--mw.log("-->"..tostring(nodeIn))
	for valuebits in predicate:gAllowValues() do
		--mw.log(node,"--|>",nodeIn)
		local node=nodeMain
		for index,name in ipairs(predicate.list) do
			node= node
				:tag("div")
				:addClass("mw-collapsible")
				:attr("id", "mw-customcollapsible-"..name)
			local value=self.collapsableValues[name] or self.collapsableDefault
			if not bit32.get(valuebits,index) then
				value=not value
			end
			if not value then
				 node:addClass("mw-collapsed")
			end
		end
		node:node(nodeIn)
	end
	return nodeMain
end
function signFunction:preFormat()
end
function signFunction:_varAdd(timeText,text,predicate)
	local index=indexOf(self.predicates,predicate)
	local tabs
	if index then
		tabs=self.tabs[index]
		--mw.log("add",self.signs[index],timeText,text)
		for index,t in ipairs(tabs.texts) do
			if timeText==tabs.times[index] and text==t then
				return
			end
		end
	else
		tabs={times={},texts={}}
		local main_index=#self.signs+1
		local valueText=predicate:valueText()
		self.signs[main_index]=valueText
		self.predicates[main_index]=predicate
		self.tabs[main_index]=tabs
		--mw.log("add",valueText,timeText,text)
	end
	local index=#tabs.texts+1
	tabs.times[index]=timeText
	tabs.texts[index]=text
end
function signFunction:preFormat_var(time,text,predicate)
	if #self.var>0 then
		predicate=predicate:limitTo(self.var) 
		--mw.log("--",predicate)
		if predicate:isReal() then
			return
		end--elseif predicate:isReal(false) then
	end
	self:_varAdd(tostring(time),text,predicate)
end
function signFunction:calculate_var()
	if #self.signs==0 then
		return
	end
	signFunction.var_array.new("module.timeLine.ex.signs",self.signs)
	--mw.log("saveCount",#self.signs)
	for index,tabs in pairs(self.tabs) do
		--mw.log("save",self.signs[index],#tabs.texts,#tabs.times)
		signFunction.var_array.new("module.timeLine.ex.texts."..self.signs[index],tabs.texts)
		signFunction.var_array.new("module.timeLine.ex.times."..self.signs[index],tabs.times)
	end
end
function signFunction:calculate()
	if self.var then
		self:calculate_var()
	end
end


--带标记时间文本
local timeLineModify=timeLine.base()--继承时间线
function timeLineModify.base(args)
	return setmetatable(args or {},timeLineModify)
end
timeLineModify.old_title=timeLine.title
function timeLineModify.title(titleLevel)
	local this=timeLineModify.old_title(titleLevel)
	this.sign=textPredicate._true
	return timeLineModify.base(this)
end
timeLine.title=timeLineModify.title
timeLineModify.__index = timeLineModify
function timeLineModify.root()
	local root=timeLineModify.base(timeLine.root())
	root.sign=textPredicate._true
	root.mark="root"
	timeLineModify.signFunction:preLoad()
	if timeLineModify.signFunction.var then
		for _,timeText,text,sign in timeLineModify.signFunction:preArgs() do
			root:insert(timeLineModify.load(timeText,text,sign))
		end
	end
	return root 
end
function timeLineModify.load(timeText,text,sign)
	local line=timeLineModify.base{time=timeLine.getTime(timeText),text=text,sign=sign}
	return line
end
function timeLineModify.create(withTimeText,parent)--带时间文本时间数字化·文本化函数
	local this=timeLine.create(withTimeText)
	if not this then return end
	this.sign,this.text=timeLineModify.signFunction:analysisText(this.text)
	this=timeLineModify.base(this)
	if parent then
		parent:insert(this)
	end
	--mw.log(this.sign,this.text,this.timeRange)
	return this
end
function timeLineModify:sub(other)--执行减去折叠显示部分操作,返回剩余的显示与被剪掉的显示
	local fs,ss=self.sign,other.sign
	fs,ss=fs:_sub(ss)
	if not ss:isReal(false) then
		local other=clone(self)
		self.sign=fs
		other.sign=ss
		return other
	end
end
function timeLineModify:testText()
	local t={"timeLineModify",tostring(self.sign),"time:"..tostring(self.time),"text:"..tostring(self.text)}
	return table.concat(t,";")	
end
function timeLineModify:move(otherLine,b,e)--辅助函数,转移数据。重写
	while e>=b do--转移数据删除
		local left=otherLine:insert(self[e])
		--mw.log("转移数据",self[e]:testText(),"到",otherLine:testText(),"剩余",left and left:testText())
		if left then
			self[e]=left
		else
			table.remove(self,e)
		end
		e=e-1
	end
end
function timeLineModify:insert(newLine)--插入时间函数   case相当于不同空间定位,需要被拆分进各个时段中。一次置入将把空间被包含部分置入,返回剩余部分
	if not newLine then return end
	local other=newLine:sub(self)
	if other then
		--mw.log("_insert",other:testText())
		timeLine.insert(self,other)--被剪掉的部分即从属的部分,应当插入其中
	end
	if not newLine.sign:isReal(false) then
		--mw.log("last",newLine:testText())
		return newLine--剩余部分返回,继续插入
	end
end
function timeLineModify:insertText(originText)--插入文本 重写
	if originText==nil or originText=="" then return end
	return timeLineModify.create(originText,self)
end
function timeLineModify:getTimeText()
	return timeLine.format:timeToText(self.time,self.parent and self.parent.time)
end
function timeLineModify:toNode(parent)--转换为html的node
	local node
	if self.titleLevel then--标题特殊处理
		node=mw.html.create()
		if self.time then-- and self.titleLevel>0
			timeLineModify.signFunction:preFormat(self.time,self.text,self.sign)
			local timeText=self:getTimeText()
			local title=timeLineModify.signFunction:formatNode(self.text and timeText..self.text or timeText,self.sign)
			node:node(module.makeTitle(title,self.titleLevel))
			parent:node(node)
		end
	else
		timeLineModify.signFunction:preFormat(self.time,self.text,self.sign)
		local timeText=self:getTimeText()
		node=mw.html.create()
		local li=timeLineModify.signFunction:formatNode(module.makeLi(timeText,self.text or ""),self.sign)
		node:node(li)
		if not parent.ul then
			parent.ul=parent:tag("ul")
		end
		parent.ul:node(node)
	end
	for _,child in ipairs(self) do
		child:toNode(node)
	end
	return node
end
function timeLineModify:__tostring()
	local text=self:toNode():done()
	timeLineModify.signFunction:calculate()
	return text
end


--主函数
function module.argsSet(args)--设置参数
	module.collapsable_default={}
	module.showIndex=((args.index or args.i or args["显示目录"])~="false")
	if module.showIndex then
		module.makeTitle=function(node,level)
			return mw.html.create("h"..level):node(node)
		end
	else
		local t={"130%","120%","110%"}
		module.makeTitle=function(node,level)
			return mw.html.create("b"):tag("font"):css("font-size",t[level]):node(node)
		end
	end
	local symbolBegin="##"
	local symbolOr="#"
	local symbolAnd="&"
	local symbolNot="-"
	textPredicate.symbolOr=symbolOr
	textPredicate.symbolAnd=symbolAnd
	textPredicate.symbolNot=symbolNot
	
	local tts=args["time-textsplit"] or args.tts or args["时间-文本分割符"] or ","
	local cw=args.columnwidth or args.cw or args["间隔宽度"] or "120px"
	if cw~="none" then
		module.makeLi=function(timeText,text)
			local node=mw.html.create("li")
			local div=node:tag("div"):css("display","flex")
			div:tag("span")
				:css("flex-shrink","0")
				:css("flex-basis",cw)
				:wikitext(timeText)
			div:tag("span"):wikitext(text)
			return node
		end
		module.makeLiSimple=function(text)
			local index=text:find(tts)
			return module.makeLi(text:sub(1,index-1),text:sub(index+1))
		end
	else
		module.makeLiSimple=function(text)
			return mw.html.create("li"):wikitext(text)
		end
		module.makeLi=function(parent,timeText,text)
			return module.makeLiSimple(parent,timeText..tts..text)
		end
	end
	local signFunction=signFunction:init{
		symbolBegin	=symbolBegin,
		symbolSplit	=symbolOr,
		collapsable	=args.collapsable or args.clps or args["折叠标签"] ,
		var			=args.var or args["传递标签"],
		auto		=args.auto or args["自动标签"],
	}
	timeLineModify.signFunction=signFunction
	args.afterrange=args.afterrange or args.ar or args["时段后符"] or ""--更改默认值
	args.prerange=args.prerange or args.pr or args["时段前符"] or "" 
	args.bullet=args.bullet or args.b or args["项目符号"] or "\*" 
	--args.timeTitle=args.timeTitle or args.tt or args["时间标题符"] or ""
	timeLineModule.argsSet(args)
end
function module.timeLineToText(args)--时间排序模式
	timeLine.format.title=""
	local root=timeLineModify.root()
	for _,sentence in preArgs(args) do
		root:insertText(sentence)
	end
	return tostring(root:sort())
end
function module._simple(args)
	local bullet=args.bullet or args.b or args["项目符号"] or "\*" 
	local level="^"..bullet.."*"
	local timeTextSplit=args["time-textsplit"] or args.tts or args["时间-文本分割符"] or ","
	local timeTitle=args.timetitle or args.tt or args["时间标题符"] or "="

	args.bullet=""
	local time={}
	function time.create(withTimeText)
		local b,e=withTimeText:find(level)
		e=e+1
		local level=e-b
		local b1,i=withTimeText:find(timeTextSplit,e)
		if not b1 then
			b1,i=withTimeText:find(timeTitle,e)
			if not b1 then
				return nil,i or 1
			end
		end
		--mw.log(withTimeText:sub(i+1),withTimeText:sub(e,b1-1))
		return setmetatable({
			level=level,
			text=withTimeText:sub(e,b1-1)
		},time),i+1
	end
	time.__index=time
	function time:supply()
		return self
	end
	time.compare={--时间比较结果常数
		before=1,--完全在之前
		equal=2,--相同
		after=3,--完全在之后
		include=4,--包含
		inside=5,--被包含
		conflict=6,--冲突
	}
	time.compare=setmetatable(time.compare,{--时间比较,理论上只允许发生在同精确度的时间上。
		__call=function(self,time1,time2)
			if not time2 then
				--mw.log(time1.text)
				return self.equal
			end
			if time1.level<time2.level then
				return self.include
			elseif time1.level>time2.level then
				return self.inside
			elseif time1.level==time2.level then
				return self.equal
			end
		end
	})
	function time:supply()
	end
	function time:__tostring()
		return self.text
	end
	local timeModule={
		time=time,
		toTime=time.create,
	}
	
	function timeLineModule.timeModule()
		return timeModule
	end
	module.argsSet(args)
	function timeLineModify.timeSupply() end
	function timeLineModify:sort()
		return self
	end
	function timeLineModify:getTimeText()
		return tostring(self.time)
	end
	return module.timeLineToText(args)
end
function module._main(args, frame)--主函数
	local sortArgs=args["排列参数"] or args.sortargs or args.sort--使用timeLine排序参数,需要配合subst使用
	if sortArgs then--返回一个sortArgs外参数相同的扩展时间线模板
		if sortArgs=="once" then--排列参数一次
			return timeLineModule.sortArgsSubst(args,"扩展时间线",nil)
		elseif sortArgs=="true" then--排列参数并返回false
			return timeLineModule.sortArgsSubst(args,"扩展时间线","false")
		--elseif sortArgs=="simple" then--简单分析模式,支持var等
			--args.analysis=args.pattern or args["格式化字符串"] or "%Y%M%(Mi)%D%(Di)%H%I%S"
			--return timeLineModule.sortArgsSubst(args,"扩展时间线","false")
		elseif sortArgs=="false" then--禁用排列,使用level方式,不支持var输入
			return module._simple(args)
		end
	else--正常模式
		module.argsSet(args)
		return module.timeLineToText(args)
	end
end
 
function module.main(frame)--主函数
	local args = getArgs(frame)
	return module._main(args, frame)
end
 
 
return module