模組: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}}
可選參數
[編輯]效果:
(當前時間戳為20250529000636,顯示第1條)

1689年波士頓起義是一場發生在1689年4月18日的起義,旨在反對新英格蘭自治領總督埃德蒙·安德羅斯爵士的統治。在自治領民兵和波士頓市民的精心組織下,「暴民」在城內逮捕自治領官員。被清教徒認為支持自治領政府統治的英國聖公會教徒也遭拘捕。不過這次起義期間雙邊都沒有出現人員傷亡。前馬薩諸塞灣殖民地的領導人重新取得政府控制權。之前被自治領政府撤職的其他殖民地政府官員重新掌握了權力。1686年獲命成為自治領總督的安德羅斯上任後無視地方代表權、對鎮民大會設限、積極推行聖公會、強制執行航海法案,還否認馬薩諸塞殖民地已有土地所有權的合法性,種種極其不得人心的行徑和英國發生的光榮革命共同影響,導致殖民地清教徒決定揭竿而起,推翻他的統治。
除了candidateList
外,有兩個可選參數timeStart
和timeInterval
。timeStart
定義輪展的起始時間(以MediaWiki時間戳規定的UTC時間,默認值19700101000000,即UTC時間1970年1月1日0時0分0秒);timeInterval
定義每輪展示的秒數(默認值86400秒,即1天)。下為示例:
(從UTC時間2025年5月29日0時0分0秒起,每小時更換一次。)
{{#invoke:Carousel|main|candidateList=User:PhiLiP/carousel-example.json|timeStart=20250529000000|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