模組:Carousel

维基百科,自由的百科全书
文档图示 模块文档[查看] [编辑] [历史] [清除缓存]

页面轮展模块,可用于首页特色条目、优良条目展示。

用法

Step 1: 创建JSON页面

首先,需要创建一个JSON页面,其中记录了要轮展嵌入的页面,以及轮展的顺序。以下是一个例子(User:PhiLiP/carousel-example.json):

[
    {
        "title": "1689年波士顿起义",
        "displayTimeRanges": [
            [
                20150304123013,
                null
            ]
        ]
    },
    {
    	"title": "孔子鸟属",
    	"displayTimeRanges": [
    		[20060313005324, 20090614110222]
    	]
    },
    {
        "title": "1850年大西洋飓风季",
        "displayTimeRanges": [
            [
                20141030151730,
                null
            ]
        ]
    },
    {
        "title": "1873年铸币法案",
        "displayTimeRanges": [
            [
                20160805143015,
                null
            ]
        ]
    },
    {
        "title": "1880年民主党全国大会",
        "displayTimeRanges": [
            [
                20141213150114,
                null
            ]
        ]
    }
]

Step 2: 调用轮展模块

效果:

Lua错误:expandTemplate: template "Wikipedia:特色条目/1873年铸币法案" does not exist。

在要显示轮展内容的位置,按下述方式调用模块:

{{#invoke:Carousel|main|candidateList=User:PhiLiP/carousel-example.json}}

可选参数

效果:

(当前时间戳为20240424014748,显示第2条)

1850年大西洋飓风季美国国家飓风中心建立的大西洋飓风数据库中未予收录的最近一个大西洋飓风季。虽然这年的气象有欠完整,但仍然有三场热带气旋对陆地构成显著影响,每场都造成一定程度的破坏。其中第一个系统于7月18日吹袭北卡罗莱纳州,造成严重破坏后又给大西洋地区带去高涨的海潮和狂风暴雨。8月22日,古巴哈瓦那受到一场强烈飓风的冲击,摧毁果林的同时还扰乱了航运,接下来又在佛罗里达州西北狭长地带登陆并带去了高涨的风暴潮。海上有一艘引水船因海况恶劣导致与大船相撞并沉没。9月7到8日,纽约州到鳕鱼角受到一场飓风的冲击,产生阵风和可观降水,造成许多船只遇险。

除了candidateList外,有两个可选参数timeStarttimeIntervaltimeStart定义轮展的起始时间(以MediaWiki时间戳规定的UTC时间,默认值19700101000000,即UTC时间1970年1月1日0时0分0秒);timeInterval定义每轮展示的秒数(默认值86400秒,即1天)。下为示例:

(从UTC时间2024年4月24日0时0分0秒起,每小时更换一次。)

{{#invoke:Carousel|main|candidateList=User:PhiLiP/carousel-example.json|timeStart=20240424000000|timeInterval=3600}}

local p = {}
local lang = mw.language.new('zh')

function tostringOrNil(value)
	if value ~= nil then
		value = tostring(value)
	end
	return value
end

function getCandidateList(pageName, currentTime)
	local page = mw.title.new(pageName)
	local candidates =
		mw.text.jsonDecode(page:getContent(), mw.text.JSON_TRY_FIXING)

	-- change mw timestamp to unix timestamp
	for _, item in pairs(candidates) do
		for i in pairs(item.displayTimeRanges) do
			item.displayTimeRanges[i][1] = tonumber(
				lang:formatDate('U', tostring(item.displayTimeRanges[i][1]))
			)
			-- use current time when the "end time" is null
			item.displayTimeRanges[i][2] = tonumber(
				lang:formatDate(
					'U', tostringOrNil(
						item.displayTimeRanges[i][2] or currentTime
					)
				)
			)
		end
	end
	return candidates
end

function pickCandidate(candidateList, currentTime, timeStart, timeInterval)
	local processedTime = timeStart
	local currentDisplayStart =
		math.floor(currentTime / timeInterval) * timeInterval
	local currentDisplayEnd = currentDisplayStart + timeInterval
	local maxCycles = 100
	mw.log("currentDisplayStart: ", currentDisplayStart)
	while maxCycles > 0 do
		local cycleItems = 0
		local nextStatusChanged = 0xffffffffffffffff
		for _, item in pairs(candidateList) do
			for _, range in pairs(item.displayTimeRanges) do
				local rangeStart =
					math.ceil(range[1] / timeInterval) * timeInterval
				local rangeEnd =
					math.ceil(range[2] / timeInterval) * timeInterval
				if rangeStart > processedTime then
					nextStatusChanged = math.min(nextStatusChanged, rangeStart)
				end
				if rangeEnd > processedTime then
					nextStatusChanged = math.min(nextStatusChanged, rangeEnd)
				end
				if processedTime < rangeStart or
					processedTime >= rangeEnd then
					-- continue
					-- mw.log(rangeStart)
					-- mw.log(rangeEnd)
					-- mw.log('---------')
				elseif processedTime >= currentDisplayStart and
					processedTime < currentDisplayEnd then
					return item.title
				else
					-- processedTime = processedTime + timeInterval
					cycleItems = cycleItems + 1
					break
				end
			end
			-- mw.log(processedTime)
		end
		mw.log("nextStatusChanged: ", nextStatusChanged)
		mw.log("CycleItems: ", cycleItems)
		mw.log("ProcessedTime (before): ", processedTime)
		if cycleItems > 0 then
			local times =
				math.ceil((nextStatusChanged - processedTime) / timeInterval)
			processedTime =
				processedTime + math.floor(times / cycleItems) *
				cycleItems * timeInterval;
			local remaining = times % cycleItems
			mw.log("remaining: ", remaining)

			-- process remaining items in the "partial" cycle
			if remaining > 0 then
				for _, item in pairs(candidateList) do
					-- mw.log(item.title)
					
					for _, range in pairs(item.displayTimeRanges) do
						-- mw.log(remaining, range[1], range[2])
						local rangeStart =
							math.ceil(range[1] / timeInterval) * timeInterval
						local rangeEnd =
							math.ceil(range[2] / timeInterval) * timeInterval
						if processedTime < rangeStart or
							processedTime > rangeEnd then
							-- continue
						--elseif remaining > 0 then
						--	remaining = remaining - 1
						--	break
						elseif processedTime >= currentDisplayStart and
							processedTime < currentDisplayEnd then
							return item.title
						else
							processedTime = processedTime + timeInterval
							break
						end
					end
				end
			end



		else
			processedTime = math.max(processedTime, nextStatusChanged)
		end
		mw.log("ProcessedTime: ", processedTime)
		maxCycles = maxCycles - 1
	end
	-- TODO: raise an error
	return 'EXCEED LIMITATION'
end

function p.getCandidate(frame)
	local args = frame.args
	local currentTime = tonumber(
		lang:formatDate('U', tostringOrNil(args.currentTime))
	)
	local candidateList = getCandidateList(
		args.candidateList,
		args.currentTime
	)
	local timeStart = tonumber(
		lang:formatDate(
			"U", tostringOrNil(args.timeStart) or '19700101000000'
		)
	)
	local timeInterval = tonumber(args.timeInterval) or 86400
	local title = args.titlePrefix or 'Wikipedia:特色条目/'
	title = title .. pickCandidate(
		candidateList, currentTime, timeStart, timeInterval)
	return title
end

function p.main(frame)
	return mw.getCurrentFrame():expandTemplate({
		title = p.getCandidate(frame)
	})
end

return p