Modul:Navseasoncats
Tampilan
Galat Lua: too many expensive function calls.
require('strict')
local p = {}
local horizontal = require("Modul:List").horizontal
--[[==========================================================================]]
--[[ Globals ]]
--[[==========================================================================]]
local currtitle = mw.title.getCurrentTitle()
local nexistingcats = 0
local errors = ""
local testcasecolon = ""
local testcases = string.match(currtitle.subpageText, "^testcases")
if testcases then
testcasecolon = ":"
end
local navborder = true
local followRs = true
local skipgaps = false
local skipgaps_limit = 25
local term_limit = 10
local hgap_limit = 6
local ygap_limit = 5
local listall = false
local tlistall = {}
local tlistallbwd = {}
local tlistallfwd = {}
local ttrackingcats = {
--when reindexing, Ctrl+H 'trackcat(13,' & 'ttrackingcats[16]'
"", -- [1] placeholder for [[Category:Category series navigation using cat parameter]]
"", -- [2] placeholder for [[Category:Category series navigation using testcase parameter]]
"", -- [3] placeholder for [[Category:Category series navigation using unknown parameter]]
"", -- [4] placeholder for [[Category:Category series navigation range not using en dash]]
"", -- [5] placeholder for [[Category:Category series navigation range abbreviated (MOS)]]
"", -- [6] placeholder for [[Category:Category series navigation range redirected (base change)]]
"", -- [7] placeholder for [[Category:Category series navigation range redirected (var change)]]
"", -- [8] placeholder for [[Category:Category series navigation range redirected (end)]]
"", -- [9] placeholder for [[Category:Category series navigation range redirected (MOS)]]
"", --[10] placeholder for [[Category:Category series navigation range redirected (other)]]
"", --[11] placeholder for [[Category:Category series navigation range gaps]]
"", --[12] placeholder for [[Category:Category series navigation range irregular]]
"", --[13] placeholder for [[Category:Category series navigation range irregular, 0-length]]
"", --[14] placeholder for [[Category:Category series navigation range ends (present)]]
"", --[15] placeholder for [[Category:Category series navigation range ends (blank, MOS)]]
"", --[16] placeholder for [[Category:Category series navigation isolated]]
"", --[17] placeholder for [[Category:Category series navigation default season gap size]]
"", --[18] placeholder for [[Category:Category series navigation decade redirected]]
"", --[19] placeholder for [[Category:Category series navigation year redirected (base change)]]
"", --[20] placeholder for [[Category:Category series navigation year redirected (var change)]]
"", --[21] placeholder for [[Category:Category series navigation year redirected (other)]]
"", --[22] placeholder for [[Category:Category series navigation roman numeral redirected]]
"", --[23] placeholder for [[Category:Category series navigation nordinal redirected]]
"", --[24] placeholder for [[Category:Category series navigation wordinal redirected]]
"", --[25] placeholder for [[Category:Category series navigation TV season redirected]]
"", --[26] placeholder for [[Category:Category series navigation using skip-gaps parameter]]
"", --[27] placeholder for [[Category:Category series navigation year and range]]
"", --[28] placeholder for [[Category:Category series navigation year and decade]]
"", --[29] placeholder for [[Category:Category series navigation decade and century]]
"", --[30] placeholder for [[Category:Category series navigation in mainspace]]
"" --[31] placeholder for [[Category:Category series navigation redirection error]]
}
local avoidself =
(not string.match(currtitle.text, "Navseasoncats with") and not string.match(currtitle.text, "Navseasoncats.*/doc") and
not string.match(currtitle.text, "Navseasoncats.*/sandbox") and
currtitle.text ~= "Navseasoncats" and
currtitle.nsText ~= "User_talk" and
currtitle.nsText ~= "Template_talk" and
(currtitle.nsText ~= "Template" or testcases)) --avoid nested transclusion errors (i.e. {{Infilmdecade}})
--[[==========================================================================]]
--[[ Utility & category functions ]]
--[[==========================================================================]]
--Determine if a category exists (in a function for easier localization).
local function catexists(title)
return mw.title.new(title, "Kategori").exists
end
--Error message handling.
function p.errorclass(msg)
return mw.text.tag(
"span",
{class = "error mw-ext-cite-error"},
"<b>Galat!</b> " .. string.gsub(msg, "&#", "&#")
)
end
--Failure handling.
function p.failedcat(errors, sortkey)
if avoidself then
return (errors or "") ..
"***Navseasoncats failed to generate navbox***" ..
"[[" ..
testcasecolon .. "Category:Navseasoncats failed to generate navbox|" .. (sortkey or "O") .. "]]\n"
end
return ""
end
--Tracking cat handling.
-- key: 15 (when reindexing ttrackingcats{}, Ctrl+H 'trackcat(13,' & 'ttrackingcats[16]')
-- cat: 'Navseasoncats isolated'; '' to remove
--Used by main, all nav_*(), & several utility functions.
local function trackcat(key, cat)
if avoidself and key and cat then
if cat ~= "" then
ttrackingcats[key] = "[[" .. testcasecolon .. "Kategori:" .. cat .. "]]"
else
ttrackingcats[key] = ""
end
end
return
end
--Check for unknown parameters.
--Used by main only.
local function checkforunknownparams(tbl)
local knownparams = {
--parameter whitelist
["min"] = "min",
["max"] = "max",
["cat"] = "cat",
["show"] = "show",
["testcase"] = "testcase",
["testcasegap"] = "testcasegap",
["skip-gaps"] = "skip-gaps",
["list-all-links"] = "list-all-links",
["follow-redirects"] = "follow-redirects"
}
for k, _ in pairs(tbl) do
if knownparams[k] == nil then
trackcat(3, "Navseasoncats dengan parameter tidak dikenal")
break
end
end
end
--Check for nav_*() navigational isolation (not necessarily an error).
--Used by all nav_*().
local function isolatedcat()
if nexistingcats == 0 then
trackcat(16, "Navseasoncats terisolasi")
end
end
--Returns the target of {{Category redirect}}, if it exists, else returns the original cat.
--{{Title year}}, etc., if found, are evaluated.
--Used by catlinkfollowr(), and so indirectly by all nav_*().
local function rtarget(frame, cat)
local catcontent = mw.title.new(cat or "", "Kategori"):getContent()
if string.match(catcontent or "", "{{ *[Cc]at") then --prelim test
local regex = {
--the following 11 pages (6 condensed) redirect to [[Template:Category redirect]], in descending order, as of 9/2022:
"{{ *[Cc]ate?g?o?r?y?[ _]*[rR]edirect", --505+312+243+1 transclusions
"{{ *[Cc]atr", --21
"{{ *[Cc]at[ _]*[rR]edir", --5+3
"{{ *[Cc]at[ _]*[rR]ed", --3+2
"{{ *[Cc]at[ _]*[mM]ove", --1
"{{ *[Cc]ategory[ _]*[mM]ove" --0
}
for _, v in pairs(regex) do
local rtarget = mw.ustring.match(catcontent, v .. "%s*|%s*([^|}]+)")
if rtarget then
if string.match(rtarget, "{{") then --{{Title year}}, etc., exists; evaluate
local regex_ty = "%s*|%s*([^{}]*{{([^{|}]+)}}[^{}]-)%s*}}" --eval null-param templates only; expanded if/as needed
local rtarget_orig, ty = mw.ustring.match(catcontent, v .. regex_ty)
if rtarget_orig then
local ty_eval = frame:expandTemplate {title = ty, args = {page = cat}} --frame:newChild doesn't work, use 'page' param instead
local rtarget_eval = mw.ustring.gsub(rtarget_orig, "{{%s*" .. ty .. "%s*}}", ty_eval)
return rtarget_eval
else --sub-parameters present; track & return default
trackcat(31, "Navseasoncats redirection error")
end
end
rtarget = mw.ustring.gsub(rtarget, "^1%s*=%s*", "")
rtarget = string.gsub(rtarget, "^[Cc]ategory:", "")
return rtarget
end
end --for
end --if
return cat
end
--Similar to {{LinkCatIfExists2}}: make a piped link to a category, if it exists;
--if it doesn't exist, just display the greyed link title without linking.
--Follows {{Category redirect}}s.
--Returns {
-- ['cat'] = cat,
-- ['catexists'] = true,
-- ['rtarget'] = <#R target>,
-- ['navelement'] = <#R target navelement>,
-- ['displaytext'] = displaytext,
-- } if #R followed;
--returns {
-- ['cat'] = cat,
-- ['catexists'] = <true|false>,
-- ['rtarget'] = nil,
-- ['navelement'] = <cat navelement>,
-- ['displaytext'] = displaytext,
-- } otherwise.
--Used by all nav_*().
local function catlinkfollowr(frame, cat, displaytext, displayend)
cat = mw.text.trim(cat or "")
displaytext = mw.text.trim(displaytext or "")
displayend = displayend or false --bool flag to override displaytext IIF the cat/target is terminal (e.g. "2021–present" or "2021–")
local grey = "#888"
local disp = cat
if displaytext ~= "" then --use 'displaytext' parameter if present
disp = mw.ustring.gsub(displaytext, "%s+%(.+$", "") --strip any trailing disambiguator
end
local link, nilorR
local exists = catexists(cat)
if exists then
nexistingcats = nexistingcats + 1
if followRs then
local R = rtarget(frame, cat) --find & follow #R
if R ~= cat then --#R followed
nilorR = R
end
if displayend then
local y, hyph, ending = mw.ustring.match(R, "^.-(%d+)([–-])(.*)$")
if ending == "sekarang" then
disp = y .. hyph .. ending
elseif ending == "" then
disp = y .. hyph .. '<span style="visibility:hidden">' .. y .. "</span>" --hidden y to match spacing
end
end
link = "[[:Category:" .. R .. "|" .. disp .. "]]"
else
link = "[[:Category:" .. cat .. "|" .. disp .. "]]"
end
else
link = '<span style="color:' .. grey .. '">' .. disp .. "</span>"
end
if listall then
if nilorR then --#R followed
table.insert(
tlistall,
"[[:Category:" .. cat .. "]] → " .. "[[:Category:" .. nilorR .. "]] (" .. link .. ")"
)
else --no #R
table.insert(tlistall, "[[:Category:" .. cat .. "]] (" .. link .. ")")
end
end
return {
["cat"] = cat,
["catexists"] = exists,
["rtarget"] = nilorR,
["navelement"] = link,
["displaytext"] = disp
}
end
--Returns a numbered list of all {{Category redirect}}s followed by catlinkfollowr() -> rtarget().
--For a nav_hyphen() cat, also returns a formatted list of all cats searched for & found, & all loop indices.
--Used by all nav_*().
local function listalllinks()
local nl = "\n# "
local out = ""
if currtitle.nsText == "Kategori" then
errors =
p.errorclass(
"The <b><code>|list-all-links=yes</code></b> parameter/utility " ..
"should not be saved in category space, only previewed."
)
out = p.failedcat(errors, "Z")
end
local bwd, fwd = "", ""
if tlistallbwd[1] then
bwd = "\n\nbackward search:\n# " .. table.concat(tlistallbwd, nl)
end
if tlistallfwd[1] then
fwd = "\n\nforward search:\n# " .. table.concat(tlistallfwd, nl)
end
if tlistall[1] then
return out .. nl .. table.concat(tlistall, nl) .. bwd .. fwd
else
return out .. nl .. "No links found!?" .. bwd .. fwd
end
end
--Returns the difference b/w 2 ints separated by endash|hyphen, nil if error.
--Used by nav_hyphen() only.
local function find_duration(cat)
local from, to = mw.ustring.match(cat, "(%d+)[–-](%d+)")
if from and to then
if to == "00" then
return nil
end --doesn't follow MOS:DATERANGE
if (#from == 4) and (#to == 2) then --1900-01
to = string.match(from, "(%d%d)%d%d") .. to --1900-1901
elseif (#from == 2) and (#to == 4) then -- 01-1902
from = string.match(to, "(%d%d)%d%d") .. from --1901-1902
end
return (tonumber(to) - tonumber(from))
end
return 0
end
--Returns the ending of a terminal cat, and sets the appropriate tracking cat, else nil.
--Used by nav_hyphen() only.
local function find_terminaltxt(cat)
local terminaltxt = nil
if mw.ustring.match(cat, "%d+[–-]sekarang$") then
terminaltxt = "sekarang"
trackcat(14, "Navseasoncats range ends (sekarang)")
elseif mw.ustring.match(cat, "%d+[–-]$") then
terminaltxt = ""
trackcat(15, "Navseasoncats range ends (blank, MOS)")
end
return terminaltxt
end
--Returns an unsigned string of the 1-4 digit decade ending in "0", else nil.
--Used by nav_decade() only.
local function sterilizedec(decade)
if decade == nil or decade == "" then
return nil
end
local dec = string.match(decade, "^[-%+]?(%d?%d?%d?0)$") or string.match(decade, "^[-%+]?(%d?%d?%d?0)%D")
if dec then
return dec
else
--fix 2-4 digit decade
local decade_fixed234 =
string.match(decade, "^[-%+]?(%d%d?%d?)%d$") or string.match(decade, "^[-%+]?(%d%d?%d?)%d%D")
if decade_fixed234 then
return decade_fixed234 .. "0"
end
--fix 1-digit decade
local decade_fixed1 = string.match(decade, "^[-%+]?(%d)$") or string.match(decade, "^[-%+]?(%d)%D")
if decade_fixed1 then
return "0"
end
--unfixable
return nil
end
end
--Check for nav_hyphen default gap size + isolatedcat() (not necessarily an error).
--Used by nav_hyphen() only.
local function defaultgapcat(bool)
if bool and nexistingcats == 0 then
--using "nexistingcats > 0" isn't as useful, since the default gap size obviously worked
trackcat(17, "Navseasoncats default season gap size")
end
end
--12 -> 12th, etc.
--Used by nav_nordinal() & nav_wordinal().
function p.addord(i)
if tonumber(i) then
local s = tostring(i)
local tens = string.match(s, "1%d$")
if tens then
return s .. "th"
end
local ones = string.match(s, "%d$")
if ones == "1" then
return s .. "st"
elseif ones == "2" then
return s .. "nd"
elseif ones == "3" then
return s .. "rd"
end
return s .. "th"
end
return i
end
--Returns the properly formatted central nav element.
--Expects an integer i, and a catlinkfollowr() table.
--Used by nav_decade() & nav_ordinal() only.
local function navcenter(i, catlink)
if i == 0 then --center nav element
if navborder == true then
return "*<b>" .. catlink.displaytext .. "</b>\n"
else
return "*<b>" .. catlink.navelement .. "</b>\n"
end
else
return "*" .. catlink.navelement .. "\n"
end
end
--Return conditionally aligned stacked navs.
--Used by main only.
local function nav1nav2(nav1, nav2)
if avoidself then
local forcealign = '<div style="display:block !important; max-width: calc(100% - 25em);">'
return forcealign .. "\n" .. nav1 .. "\n" .. nav2 .. "\n</div>"
else
return nav1 .. "\n" .. nav2
end
end
--[[==========================================================================]]
--[[ Formerly separated templates/modules ]]
--[[==========================================================================]]
--[[==========================={{ nav_hyphen }}=============================]]
local function nav_hyphen(frame, start, hyph, finish, firstpart, lastpart, minseas, maxseas, testgap)
--Expects a PAGENAME of the form "Some sequential 2015–16 example cat", where
-- start = 2015
-- hyph = –
-- finish = 16 (sequential years can be abbreviated, but others should be full year, e.g. "2001–2005")
-- firstpart = Some sequential
-- lastpart = example cat
-- minseas = 1800 ('min' starting season shown; optional; defaults to -9999)
-- maxseas = 2000 ('max' starting season shown; optional; defaults to 9999; 2000 will show 2000-01)
-- testgap = 0 (testcasegap parameter for easier testing; optional)
--sterilize start
if string.match(start or "", "^%d%d?%d?%d?$") == nil then --1-4 digits, AD only
local start_fixed = mw.ustring.match(start or "", "^%s*(%d%d?%d?%d?)%D")
if start_fixed then
start = start_fixed
else
errors =
p.errorclass(
'Function nav_hyphen can\'t recognize the number "' ..
(start or "") ..
'" ' ..
'in the first part of the "season" that was passed to it. ' ..
'For e.g. "2015–16", "2015" is expected via "|2015|–|16|".'
)
return p.failedcat(errors, "H")
end
end
local nstart = tonumber(start)
--en dash check
if hyph ~= "–" then
trackcat(4, "Navseasoncats range not using en dash") --nav still processable, but track
end
--sterilize finish & check for weird parents
local tgaps = {} --table of gap sizes found b/w terms { [<gap size found>] = 1 }
local ttlens = {} --table of term lengths found w/i terms { [<term length found>] = 1 }
local tirregs = {} --table of ir/regular-term-length cats' "from"s & "to"s found
local regularparent = true
if
(finish == -1) or (finish == 0) --"Members of the Scottish Parliament 2021–present"
then --"Members of the Scottish Parliament 2021–"
regularparent = false
if maxseas == nil or maxseas == "" then
maxseas = start --hide subsequent ranges
end
if finish == -1 then
trackcat(14, "Navseasoncats range ends (present)")
else
trackcat(15, "Navseasoncats range ends (blank, MOS)")
end
elseif (start == finish) and (ttrackingcats[16] ~= "") then --nav_year found isolated; check for surrounding hyphenated terms (e.g. UK MPs 1974)
trackcat(16, "") --reset for another check later
trackcat(13, "Navseasoncats range irregular, 0-length")
ttlens[0] = 1 --calc ttlens for std cases below
regularparent = "isolated"
end
if (string.match(finish or "", "^%d+$") == nil) and (string.match(finish or "", "^%-%d+$") == nil) then
local finish_fixed = mw.ustring.match(finish or "", "^%s*(%d%d?%d?%d?)%D")
if finish_fixed then
finish = finish_fixed
else
errors =
p.errorclass(
'Function nav_hyphen can\'t recognize "' ..
(finish or "") ..
'" ' ..
'in the second part of the "season" that was passed to it. ' ..
'For e.g. "2015–16", "16" is expected via "|2015|–|16|".'
)
return p.failedcat(errors, "I")
end
else
if string.len(finish) >= 5 then
errors =
p.errorclass(
'The second part of the season passed to function nav_hyphen should only be four or fewer digits, not "' ..
(finish or "") .. '". ' .. "See [[MOS:DATERANGE]] for details."
)
return p.failedcat(errors, "J")
end
end
local nfinish = tonumber(finish)
--save sterilized parent range for easier lookup later
tirregs["from0"] = nstart
tirregs["to0"] = nfinish
--sterilize min/max
local nminseas_default = -9999
local nmaxseas_default = 9999
local nminseas = tonumber(minseas) or nminseas_default --same behavior as nav_year
local nmaxseas = tonumber(maxseas) or nmaxseas_default --same behavior as nav_year
if nminseas > nstart then
nminseas = nstart
end
if nmaxseas < nstart then
nmaxseas = nstart
end
local lspace = " " --assume a leading space (most common)
local tspace = " " --assume a trailing space (most common)
if string.match(firstpart, "%($") then
lspace = ""
end --DNE for "Madrid city councillors (2007–2011)"-type cats
if string.match(lastpart, "^%)") then
tspace = ""
end --DNE for "Madrid city councillors (2007–2011)"-type cats
--calculate term length/intRAseason size & finishing year
local term_limit = 10
local t = 1
while t <= term_limit and regularparent == true do
local nish = nstart + t --use switchADBC to flip this sign to work for years BC, if/when the time comes
if (nish == nfinish) or (string.match(nish, "%d?%d$") == finish) then
ttlens[t] = 1
break
end
if t == term_limit then
errors =
p.errorclass(
'Function nav_hyphen can\'t determine a reasonable term length for "' .. start .. hyph .. finish .. '".'
)
return p.failedcat(errors, "K")
end
t = t + 1
end
--apply MOS:DATERANGE to parent
local lenstart = string.len(start)
local lenfinish = string.len(finish)
if lenstart == 4 and regularparent == true then --"2001–..."
if t == 1 then --"2001–02" & "2001–2002" both allowed
if lenfinish ~= 2 and lenfinish ~= 4 then
errors =
p.errorclass(
'The second part of the season passed to function nav_hyphen should be two or four digits, not "' ..
finish .. '".'
)
return p.failedcat(errors, "L")
end
else --"2001–2005" is required for t > 1; track "2001–05"; anything else = error
if lenfinish == 2 then
trackcat(5, "Navseasoncats range abbreviated (MOS)")
elseif lenfinish ~= 4 then
errors =
p.errorclass(
'The second part of the season passed to function nav_hyphen should be four digits, not "' ..
finish .. '".'
)
return p.failedcat(errors, "M")
end
end
if finish == "00" then --full year required regardless of term length
trackcat(5, "Navseasoncats range abbreviated (MOS)")
end
end
--calculate intERseason gap size
local hgap_default = 0 --assume & start at the most common case: 2001–02 -> 2002–03, etc.
local hgap_limit_reg = 6 --less expensive per-increment (inc x 4)
local hgap_limit_irreg = 6 --more expensive per-increment (inc x 23: inc x (k_bwd + k_fwd) = inc x (12 + 11))
local hgap_success = false
local hgap = hgap_default
while hgap <= hgap_limit_reg and regularparent == true do --verify
local prevseason2 =
firstpart ..
lspace .. (nstart - t - hgap) .. hyph .. string.match(nstart - hgap, "%d?%d$") .. tspace .. lastpart
local nextseason2 =
firstpart ..
lspace .. (nstart + t + hgap) .. hyph .. string.match(nstart + 2 * t + hgap, "%d?%d$") .. tspace .. lastpart
local prevseason4 = firstpart .. lspace .. (nstart - t - hgap) .. hyph .. (nstart - hgap) .. tspace .. lastpart
local nextseason4 =
firstpart .. lspace .. (nstart + t + hgap) .. hyph .. (nstart + 2 * t + hgap) .. tspace .. lastpart
if t == 1 then --test abbreviated range first, then full range, to be frugal with expensive functions
if
catexists(prevseason2) or --use 'or', in case we're at the edge of the cat structure,
catexists(nextseason2) or --or we hit a "–00"/"–2000" situation on one side
catexists(prevseason4) or
catexists(nextseason4)
then
hgap_success = true
break
end
elseif t > 1 then --test full range first, then abbreviated range, to be frugal with expensive functions
if
catexists(prevseason4) or --use 'or', in case we're at the edge of the cat structure,
catexists(nextseason4) or --or we hit a "–00"/"–2000" situation on one side
catexists(prevseason2) or
catexists(nextseason2)
then
hgap_success = true
break
end
end
hgap = hgap + 1
end
if hgap_success == false then
hgap = tonumber(testgap) or hgap_default --tracked via defaultgapcat()
end
--preliminary scan to determine ir/regular spacing of nearby cats;
--to limit expensive function calls, MOS:DATERANGE-violating cats are ignored;
--an irregular-term-length series should follow "YYYY..hyph..YYYY" throughout
if hgap <= hgap_limit_reg then --also to isolate temp vars
--find # of nav-visible ir/regular-term-length cats
local bwanchor = nstart --backward anchor/common year
local fwanchor = bwanchor + t --forward anchor/common year
if regularparent == "isolated" then
fwanchor = bwanchor
end
local spangreen = '[<span style="color:green">j, g, k = ' --used for/when debugging via list-all-links=yes
local spanblue = '<span style="color:blue">'
local spanred = ' (<span style="color:red">'
local span = "</span>"
local lastg = nil --to check for run-on searches
local lastk = nil --to check for run-on searches
local endfound = false --switch used to stop searching forward
local iirregs = 0 --index of tirregs[] for j < 0, since search starts from parent
local j = -3 --index of tirregs[] for j > 0 & pseudo nav position
while j <= 3 do
if j < 0 then --search backward from parent
local gbreak = false --switch used to break out of g-loop
local g = 0 --gap size
while g <= hgap_limit_irreg do
local k = 0 --term length: 0 = "0-length", 1+ = normal
while k <= term_limit do
local from = bwanchor - k - g
local to = bwanchor - g
local full = mw.text.trim(firstpart .. lspace .. from .. hyph .. to .. tspace .. lastpart)
if k == 0 then
if regularparent ~= "isolated" then --+restrict to g == 0 if repeating year problems arise
to = "0-length"
full = mw.text.trim(firstpart .. lspace .. from .. tspace .. lastpart)
if catlinkfollowr(frame, full).rtarget ~= nil then --#R followed
table.insert(
tlistallbwd,
spangreen ..
j ..
", " ..
g ..
", " ..
k ..
span ..
"] " ..
full .. spanred .. "#R ignored" .. span .. ")"
)
full, to = "", "" --don't use/follow 0-length cat #Rs from nav_hyphen(); otherwise gets messy
end
end
end
if
(k >= 1) or (to == "0-length") --the normal case; only continue k = 0 if 0-length found
then --ghetto "continue" (thx Lua) to avoid expensive searches for "UK MPs 1974-1974", etc.
table.insert(tlistallbwd, spangreen .. j .. ", " .. g .. ", " .. k .. span .. "] " .. full)
if (k == 1) and (g == 0 or g == 1) and (catexists(full) == false) then --allow bare-bones MOS:DATERANGE alternation, in case we're on a 0|1-gap, 1-year term series
local to2 = string.match(to, "%d%d$")
if to2 and to2 ~= "00" then --and not at a century transition (i.e. 1999–2000)
to = to2
full = mw.text.trim(firstpart .. lspace .. from .. hyph .. to .. tspace .. lastpart)
table.insert(
tlistallbwd,
spangreen .. j .. ", " .. g .. ", " .. k .. span .. "] " .. full
)
end
end
if catexists(full) then
if to == "0-length" then
trackcat(13, "Navseasoncats range irregular, 0-length")
end
tlistallbwd[#tlistallbwd] = spanblue .. tlistallbwd[#tlistallbwd] .. span .. " (found)"
ttlens[find_duration(full)] = 1
tgaps[g] = 1
iirregs = iirregs + 1
tirregs["from-" .. iirregs] = from
tirregs["to-" .. iirregs] = to
bwanchor = from --ratchet down
if to ~= "0-length" then
gbreak = true
break
else
g = 0 --soft-reset g, to keep stepping thru k
j = j + 1 --save, but keep searching thru k
if j > 3 then --lest we keep searching & finding 0-length cats ("MEPs for the Republic of Ireland 1973" & down)
gbreak = true
break
end
end
end
end --ghetto "continue"
k = k + 1
lastk = k
end --while k
if gbreak == true then
break
end
g = g + 1
lastg = g
end --while g
end --if j < 0
if j > 0 and endfound == false then --search forward from parent
local gbreak = false --switch used to break out of g-loop
local g = 0 --gap size
while g <= hgap_limit_irreg do
local k = -2 --term length: -2 = "0-length", -1 = "2020–present", 0 = "2020–", 1+ = normal
while k <= term_limit do
local from = fwanchor + g
local to4 = fwanchor + k + g --override carefully
local to2 = nil --last 2 digits of to4, IIF exists
if k == -1 then
to4 = "present" --see if end-cat exists (present)
elseif k == 0 then
to4 = ""
end --see if end-cat exists (blank)
local full = mw.text.trim(firstpart .. lspace .. from .. hyph .. to4 .. tspace .. lastpart)
if k == -2 then
if regularparent ~= "isolated" then --+restrict to g == 0 if repeating year problems arise
to4 = "0-length" --see if 0-length cat exists
full = mw.text.trim(firstpart .. lspace .. from .. tspace .. lastpart)
if catlinkfollowr(frame, full).rtarget ~= nil then --#R followed
table.insert(
tlistallfwd,
spangreen ..
j ..
", " ..
g ..
", " ..
k ..
span ..
"] " ..
full .. spanred .. "#R ignored" .. span .. ")"
)
full, to4 = "", "" --don't use/follow 0-length cat #Rs from nav_hyphen(); otherwise gets messy
end
end
end
if
(k >= -1) or (to4 == "0-length") --only continue k = -2 if 0-length found
then --ghetto "continue" (thx Lua) to avoid expensive searches for "UK MPs 1974-1974", etc.
table.insert(tlistallfwd, spangreen .. j .. ", " .. g .. ", " .. k .. span .. "] " .. full)
if (k == 1) and (g == 0 or g == 1) and (catexists(full) == false) then --allow bare-bones MOS:DATERANGE alternation, in case we're on a 0|1-gap, 1-year term series
to2 = string.match(to4, "%d%d$")
if to2 and to2 ~= "00" then --and not at a century transition (i.e. 1999–2000)
full =
mw.text.trim(firstpart .. lspace .. from .. hyph .. to2 .. tspace .. lastpart)
table.insert(
tlistallfwd,
spangreen .. j .. ", " .. g .. ", " .. k .. span .. "] " .. full
)
end
end
if catexists(full) then
if to4 == "0-length" then
if rtarget(frame, full) == full then --only use 0-length cats that don't #R
trackcat(13, "Navseasoncats range irregular, 0-length")
end
end
tirregs["from" .. j] = from
tirregs["to" .. j] = (to2 or to4)
if (k == -1) or (k == 0) then
endfound = true --tentative
else --k == { -2, > 0 }
tlistallfwd[#tlistallfwd] =
spanblue .. tlistallfwd[#tlistallfwd] .. span .. " (found)"
ttlens[find_duration(full)] = 1
tgaps[g] = 1
endfound = false
if to4 ~= "0-length" then --k > 0
fwanchor = to4 --ratchet up
gbreak = true
break --only break on k > 0 b/c old end-cat #Rs still exist like "Members of the Scottish Parliament 2011–"
else --k == -2
j = j + 1 --save, but keep searching k's, in case "1974" → "1974-1979"
if j > 3 then --lest we keep searching & finding 0-length cats ("2018 CONCACAF Champions League" & up)
gbreak = true
break
end
end
end
end
end --ghetto "continue"
k = k + 1
lastk = k
end --while k
if gbreak == true then
break
end
g = g + 1
lastg = g
end --while g
end --if j > 0
if (lastg == (hgap_limit_irreg + 1)) and (lastk == (term_limit + 1)) then --search exhausted
if j < 0 then
j = 0 --bwd search exhausted; continue fwd
elseif j > 0 then
break
end --fwd search exhausted
end
j = j + 1
end --while j <= 3
end --if hgap <= hgap_limit_reg
--begin navhyphen
local navh = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n' .. "|\n"
local terminalcat = false --switch used to hide future cats
local terminaltxt = nil
local i = -3 --nav position
while i <= 3 do
local from = nstart + i * (t + hgap) --the logical, but not necessarily correct, 'from'
if tirregs["from" .. i] then
from = tonumber(tirregs["from" .. i])
end --prefer irregular term table
local from2 = string.match(from, "%d?%d$")
local to = tostring(from + t) --the logical, naive range, but
if tirregs["to" .. i] then --prefer irregular term table
to = tirregs["to" .. i]
elseif regularparent == false and tirregs and i > 0 then
to = tirregs["to-1"] --special treatment for parent terminal cats, since they have no natural 'to'
end
local to2 = string.match(to, "%d?%d$")
local tofinal = (to2 or "") --assume t=1 and abbreviated 'to' (the most common case)
if
t > 1 or (from2 - (to2 or from2)) > 0 --per MOS:DATERANGE (e.g. 1999-2004)
then --century transition exception (e.g. 1999–2000)
tofinal = (to or "") --default to the MOS-correct format, in case no fallbacks found
end
if to == "0-length" then
tofinal = to
end
--check existance of 4-digit, MOS-correct range, with abbreviation fallback
if tofinal ~= "0-length" then
if t > 1 and string.len(from) == 4 then --e.g. 1999-2004
--determine which link exists (full or abbr)
local full = firstpart .. lspace .. from .. hyph .. tofinal .. tspace .. lastpart
if not catexists(full) then
local abbr = firstpart .. lspace .. from .. hyph .. to2 .. tspace .. lastpart
if catexists(abbr) then
tofinal = (to2 or "") --rv to MOS-incorrect format; if full AND abbr DNE, then tofinal is still in its MOS-correct format
end
end
elseif t == 1 then --full-year consecutive ranges are also allowed
local abbr = firstpart .. lspace .. from .. hyph .. tofinal .. tspace .. lastpart --assume tofinal is in abbr format
if not catexists(abbr) and tofinal ~= to then
local full = firstpart .. lspace .. from .. hyph .. to .. tspace .. lastpart
if catexists(full) then
tofinal = (to or "") --if abbr AND full DNE, then tofinal is still in its abbr format (unless it's a century transition)
end
end
end
end
--populate navh
if i ~= 0 then --left/right navh
local orig = firstpart .. lspace .. from .. hyph .. tofinal .. tspace .. lastpart
local disp = from .. hyph .. tofinal
if tofinal == "0-length" then
orig = firstpart .. lspace .. from .. tspace .. lastpart
disp = from
end
local catlink = catlinkfollowr(frame, orig, disp, true) --force terminal cat display
if terminalcat == false then
terminaltxt = find_terminaltxt(disp) --also sets tracking cats
terminalcat = (terminaltxt ~= nil)
end
if catlink.rtarget and avoidself then --a {{Category redirect}} was followed, figure out why
--determine new term length & gap size
ttlens[find_duration(catlink.rtarget)] = 1
if i > -3 then
local lastto = tirregs["to" .. (i - 1)]
if lastto == nil then
local lastfrom = nstart + (i - 1) * (t + hgap)
lastto = lastfrom + t --use last logical 'from' to calc lastto
end
if lastto then
local gapcat = lastto .. "-" .. from --dummy cat to calc with
local gap = find_duration(gapcat) or -1 --in case of nil,
tgaps[gap] = 1 --tgaps[-1] is ignored
end
end
--display/tracking handling
local base_regex = "%d+[–-]%d+"
local origbase = mw.ustring.gsub(orig, base_regex, "")
local rtarbase, rtarbase_success = mw.ustring.gsub(catlink.rtarget, base_regex, "")
if rtarbase_success == 0 then
local base_regex_lax = "%d%d%d%d" --in case rtarget is a year cat
rtarbase, rtarbase_success = mw.ustring.gsub(catlink.rtarget, base_regex_lax, "")
end
local terminal_regex = "%d+[–-]" .. (terminaltxt or "") .. "$" --more manual ORs bc Lua regex sux
if mw.ustring.match(orig, terminal_regex) then
origbase = mw.ustring.gsub(orig, terminal_regex, "")
end
if mw.ustring.match(catlink.rtarget, terminal_regex) then
--finagle/overload terminalcat type to set nmaxseas on 1st occurence only
if terminalcat == false then
terminalcat = 1
end
local dummy = find_terminaltxt(catlink.rtarget) --also sets tracking cats
rtarbase = mw.ustring.gsub(catlink.rtarget, terminal_regex, "")
end
origbase = mw.text.trim(origbase)
rtarbase = mw.text.trim(rtarbase)
if origbase ~= rtarbase then
trackcat(6, "Navseasoncats range redirected (base change)")
elseif terminalcat == 1 then
trackcat(8, "Navseasoncats range redirected (end)")
else --origbase == rtarbase
local all4s_regex = "%d%d%d%d[–-]%d%d%d%d"
local orig_all4s = mw.ustring.match(orig, all4s_regex)
local rtar_all4s = mw.ustring.match(catlink.rtarget, all4s_regex)
if orig_all4s and rtar_all4s then
trackcat(10, "Navseasoncats range redirected (other)")
else
local year_regex1 = "%d%d%d%d$"
local year_regex2 = "%d%d%d%d[%s%)]"
local year_rtar =
mw.ustring.match(catlink.rtarget, year_regex1) or
mw.ustring.match(catlink.rtarget, year_regex2)
if orig_all4s and year_rtar then
trackcat(7, "Navseasoncats range redirected (var change)")
else
trackcat(9, "Navseasoncats range redirected (MOS)")
end
end
end
end
if terminalcat then --true or 1
if type(terminalcat) ~= "boolean" then
nmaxseas = from
end --only want to do this once
terminalcat = true --done finagling/overloading
end
if (from >= 0) and (nminseas <= from) and (from <= nmaxseas) then
navh = navh .. "*" .. catlink.navelement .. "\n"
if terminalcat then
nmaxseas = nminseas_default
end --prevent display of future ranges
else
local hidden = '<span style="visibility:hidden">' .. disp .. "</span>"
navh = navh .. "*" .. hidden .. "\n"
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. " (" .. hidden .. ")"
end
end
else --center navh
if finish == -1 then
finish = "sekarang"
elseif finish == 0 then
finish = '<span style="visibility:hidden">' .. start .. "</span>"
end
local disp = start .. hyph .. finish
if regularparent == "isolated" then
disp = start
end
navh = navh .. "*<b>" .. disp .. "</b>\n"
end
i = i + 1
end
--tracking cats & finalize
if avoidself then
local igaps = 0 --# of diff gap sizes > 0 found
local itlens = 0 --# of diff term lengths found
for s = 1, hgap_limit_reg do --must loop; #tgaps, #ttlens unreliable
igaps = igaps + (tgaps[s] or 0)
end
for s = 0, term_limit do
itlens = itlens + (ttlens[s] or 0)
end
if igaps > 0 then
trackcat(11, "Navseasoncats range gaps")
end
if itlens > 1 and ttrackingcats[13] == "" then --avoid duplication in "Navseasoncats range irregular, 0-length"
trackcat(12, "Navseasoncats range irregular")
end
end
isolatedcat()
defaultgapcat(not hgap_success)
if listall then
return listalllinks()
else
return navh .. "|}"
end
end
--[[=========================={{ nav_tvseason }}============================]]
local function nav_tvseason(frame, firstpart, tv, lastpart, maximumtv)
--Expects a PAGENAME of the form "Futurama (season 1) episodes", where
-- firstpart = Futurama (season
-- tv = 1
-- lastpart = ) episodes
-- maximumtv = 7 ('max' tv season parameter; optional; defaults to 9999)
tv = tonumber(tv)
if tv == nil then
errors = p.errorclass("Function nav_tvseason can't recognize the TV season number sent to its 2nd parameter.")
return p.failedcat(errors, "T")
end
local maxtv = tonumber(maximumtv) or 9999 --allow +/- qualifier
if maxtv < tv then
maxtv = tv
end --input error; maxtv should be >= parent
--begin navtvseason
local navt = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n' .. "|\n"
local i = -5 --nav position
while i <= 5 do
local t = tv + i
if i ~= 0 then --left/right navt
local catlink = catlinkfollowr(frame, firstpart .. " " .. t .. lastpart, t)
if (t >= 1 and t <= maxtv) then --hardcode mintv
if catlink.rtarget then --a {{Category redirect}} was followed
trackcat(25, "Navseasoncats TV season redirected")
end
navt = navt .. "*" .. catlink.navelement .. "\n"
else
local hidden = '<span style="visibility:hidden">' .. "0" .. "</span>" --'0' to maintain dot spacing
navt = navt .. "*" .. hidden .. "\n"
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. " (" .. hidden .. ")"
end
end
else --center navt
navt = navt .. "*<b>" .. tv .. "</b>\n"
end
i = i + 1
end
isolatedcat()
if listall then
return listalllinks()
else
return navt .. "|}"
end
end
--[[==========================={{ nav_decade }}=============================]]
local function nav_decade(frame, firstpart, decade, lastpart, mindecade, maxdecade)
-- Mengharapkan PAGENAME dengan format "Beberapa contoh kucing 2000 berurutan", dimana
-- firstpart = Beberapa contoh kucing
-- decade = 2000
-- lastpart = berurutan
-- mindecade = 1800 (parameter dekade minimal; opsional; defaultnya -9999)
-- maxdecade = 2020 (parameter dekade maksimal; opsional; defaultnya 9999)
-- Mensterilkan dekade
local dec = sterilizedec(decade)
if dec == nil then
errors = p.errorclass('Fungsi nav_decade menerima "'..(decade or '')..'" sebagai parameter ke-2, '..
'tetapi mengharapkan tahun berakhir dengan "0" sebanyak 1 hingga 4 digit.')
return p.failedcat(errors, 'D')
end
local ndec = tonumber(dec)
-- Mensterilkan mindecade & menentukan SM/M
local mindefault = '-9999'
local mindec = sterilizedec(mindecade) -- mengembalikan tostring(unsigned int), atau nil
if mindec then
if string.match(mindecade, '-%d') or
string.match(mindecade, 'SM')
then
mindec = '-'..mindec -- penanganan string dengan +/-0
end
elseif mindec == nil and mindecade and mindecade ~= '' then
errors = p.errorclass('Fungsi nav_decade menerima "'..(mindecade or '')..'" sebagai parameter ke-4, '..
'tetapi mengharapkan tahun berakhir dengan "0" sebanyak 1 hingga 4 digit, dekade terawal yang ditampilkan.')
return p.failedcat(errors, 'E')
else -- mindec == nil
mindec = mindefault -- tonumber() nanti, setelah pengecekan error
end
-- Mensterilkan maxdecade & menentukan SM/M
local maxdefault = '9999'
local maxdec = sterilizedec(maxdecade) -- mengembalikan tostring(unsigned int), atau nil + error
if maxdec then
if string.match(maxdecade, '-%d') or
string.match(maxdecade, 'SM')
then
maxdec = '-'..maxdec -- penanganan string dengan +/-0
end
elseif maxdec == nil and maxdecade and maxdecade ~= '' then
errors = p.errorclass('Fungsi nav_decade menerima "'..(maxdecade or '')..'" sebagai parameter ke-5, '..
'tetapi mengharapkan tahun berakhir dengan "0" sebanyak 1 hingga 4 digit, dekade tertinggi yang ditampilkan.')
return p.failedcat(errors, 'F')
else -- maxdec == nil
maxdec = maxdefault
end
local tspace = ' ' -- asumsikan spasi di akhir untuk kategori "1950-an di X"
if string.match(lastpart, '^-') then tspace = '' end -- tidak ada spasi untuk kategori tipe "terkait 1970-an"
-- Pengaturan SM/M dan variabel terkait
local parentSM = string.match(lastpart, '^SM') -- mengikuti konvensi "0-an SM" untuk semua tahun SM
lastpart = mw.ustring.gsub(lastpart, '^SM%s*', '') -- tangani SM secara terpisah; M tidak pernah digunakan
--TODO?: tangani BCE, tetapi hanya jika ada dalam praktik
local dec0to40M = (ndec >= 0 and ndec <= 40 and not parentSM) -- perilaku khusus dalam rentang ini
local switchSM = 1 -- 1=M parent
if parentSM then switchSM = -1 end -- -1=SM parent; mungkin disesuaikan nanti
local SMdisp = ''
local D = -math.huge -- pengalih sekunder & iterator untuk transisi M/SM
-- Periksa min/max non-default dengan lebih teliti
if mindec ~= mindefault then
if tonumber(mindec) > ndec * switchSM then
mindec = tostring(ndec * switchSM) -- kesalahan input; mindec harus <= parent
end
end
if maxdec ~= maxdefault then
if tonumber(maxdec) < ndec * switchSM then
maxdec = tostring(ndec * switchSM) -- kesalahan input; maxdec harus >= parent
end
end
local nmindec = tonumber(mindec) -- perilaku serupa dengan nav_year & nav_nordinal
local nmaxdec = tonumber(maxdec) -- perilaku serupa dengan nav_nordinal
-- Memulai nav_decade
local bnb = '' -- border/tanpa border
if navborder == false then -- untuk navigasi seri kategori tahun dan dekade
bnb = 'categorySeriesNavigation-range-transparent'
end
local navd = '<div class="toccolours categorySeriesNavigation-range '..bnb..'">\n'
local navlist = {}
local i = -50 -- posisi navigasi x 10
while i <= 50 do
local d = ndec + i * switchSM
local SM = ''
SMdisp = ''
if dec0to40M then
if D < -10 then
d = math.abs(d + 10) -- karena ada 2 dekade "0-an": "0-an SM" & "0-an" (M)
SM = 'SM '
if d == 0 then
D = -10 -- lacak penggunaan d = 0 pertama kali (SM)
end
elseif D >= -10 then
D = D + 10 -- sekarang iterasi dari 0-an M
d = D -- penggunaan d = 0 kedua
end
elseif parentSM then
if switchSM == -1 then -- parentSM melihat sisi SM (kasus umum)
SM = 'SM '
if d == 0 then -- persiapan untuk beralih ke sisi M pada iterasi berikutnya
switchSM = 1 -- penggunaan d = 0 pertama kali (SM)
D = -10 -- persiapan
end
elseif switchSM == 1 then -- telah beralih ke sisi M
D = D + 10 -- sekarang iterasi dari 0-an M
d = D -- penggunaan d = 0 kedua (pada penggunaan pertama)
end
end
if SM ~= '' and ndec <= 50 then
SMdisp = ' SM' -- tampilkan SM untuk semua dekade SM ketika "0-an" ditampilkan di navigasi
end
-- Menentukan kategori target
local disp = d..'-an'..SMdisp
local catlink = catlinkfollowr(frame, firstpart..' '..d..'-an'..tspace..SM..lastpart, disp)
if catlink.rtarget then -- sebuah {{Category redirect}} diikuti
trackcat(18, 'Navigasi seri kategori dekade dialihkan')
end
-- Mengisi navigasi kiri/kanan
local shown = navcenter(i, catlink)
local hidden = '<span style="visibility:hidden">'..disp..'</span>'
local dsign = d -- gunakan d untuk tampilan & dsign untuk logika
if SM ~= '' then dsign = -dsign end
if (nmindec <= dsign) and (dsign <= nmaxdec) then
if dsign == 0 and (nmindec == 0 or nmaxdec == 0) then -- membedakan antara -0 (SM) & 0 (M)
-- "zoom in" pada +/- 0 dan ubah dsign/min/max sementara menjadi +/- 1 untuk pemrosesan yang lebih mudah
local zsign, zmin, zmax = 1, nmindec, nmaxdec
if SM ~= '' then zsign = -1 end
if mindec == '-0' then zmin = -1
elseif mindec == '0' then zmin = 1 end
if maxdec == '-0' then zmax = -1
elseif maxdec == '0' then zmax = 1 end
if (zmin <= zsign) and (zsign <= zmax) then
table.insert(navlist, shown)
hidden = nil
else
table.insert(navlist, hidden)
end
else
table.insert(navlist, shown) -- kasus umum
hidden = nil
end
else
table.insert(navlist, hidden)
end
if listall and hidden then
tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')'
end
i = i + 10
end
-- Menambahkan daftar
navd = navd..horizontal(navlist)..'\n'
isolatedcat()
if listall then
return listalllinks()
else
return navd..'</div>'
end
end
--[[============================{{ nav_year }}==============================]]
local function nav_year(frame, firstpart, year, lastpart, minimumyear, maximumyear)
-- Mengharapkan PAGENAME dengan format "Beberapa contoh kucing 1760 berurutan", dimana
-- firstpart = Beberapa contoh kucing
-- year = 1760
-- lastpart = berurutan
-- minimumyear = 1758 (parameter tahun minimal; opsional)
-- maximumyear = 1800 (parameter tahun maksimal; opsional)
local minyear_default = -9999
local maxyear_default = 9999
year = tonumber(year) or tonumber(mw.ustring.match(year or '', '^%s*(%d*)'))
local minyear = tonumber(string.match(minimumyear or '', '-?%d+')) or minyear_default -- mengizinkan qualifier +/-
local maxyear = tonumber(string.match(maximumyear or '', '-?%d+')) or maxyear_default -- mengizinkan qualifier +/-
if string.match(minimumyear or '', 'SM') then minyear = -math.abs(minyear) end -- mengizinkan qualifier SM (AD diasumsikan)
if string.match(maximumyear or '', 'SM') then maxyear = -math.abs(maxyear) end -- mengizinkan qualifier SM (AD diasumsikan)
if year == nil then
errors = p.errorclass('Fungsi nav_year tidak dapat mengenali tahun yang dikirimkan ke parameter ke-3.')
return p.failedcat(errors, 'Y')
end
-- Pengaturan SM/M dan variabel terkait
local yearSMElastparts = { -- diperlukan untuk parent = M 1-5, ketika format SM/E tidak diketahui
-- "SME" dihapus untuk mencocokkan baik kategori M & SM; lebih mudah & lebih cepat daripada beberapa string.match()
['contoh_peoples_Hebrew_example'] = 'SME', -- format entri contoh; tambahkan & sesuaikan sesuai kebutuhan
}
local parentM = string.match(firstpart, 'M$') -- mengikuti konvensi "M 1" dari M 1 hingga M 10
local parentSM = string.match(lastpart, '^SME?') -- mengikuti konvensi "1 SM" untuk semua tahun SM
firstpart = mw.ustring.gsub(firstpart, '%s*M$', '') -- tangani M/SM secara terpisah untuk akuntansi yang lebih mudah & cepat
lastpart = mw.ustring.gsub(lastpart, '^SME?%s*', '')
local SMe = parentSM or yearSMElastparts[lastpart] or 'SM' -- default "SM"
local year1to10 = (year >= 1 and year <= 10)
local year1to10SM = year1to10 and (parentSM or parentM) -- perilaku khusus 1-10 untuk seri non-tahun dengan angka rendah
local year1to15M = (year >= 1 and year <= 15 and not parentSM) -- perilaku khusus 1-15 untuk tampilan M/SM
local switchMSM = 1 -- 1=M parent
if parentSM then switchMSM = -1 end -- -1=SM parent; mungkin disesuaikan nanti
local Y = 0 -- iterator sekunder untuk parent SM yang menampilkan M
if minyear > year * switchMSM then minyear = year * switchMSM end -- kesalahan input; minyear harus <= parent
if maxyear < year * switchMSM then maxyear = year * switchMSM end -- kesalahan input; maxyear harus >= parent
local lspace = ' ' -- spasi di depan tahun, setelah firstpart
if string.match(firstpart, '[%-VW]$') then
lspace = '' -- misalnya "Mesin Straight-8"
end
local tspace = ' ' -- spasi di akhir tahun, sebelum lastpart
if string.match(lastpart, '^-') then
tspace = '' -- misalnya "timeline terkait-2018"
end
-- menentukan ukuran jarak antar tahun untuk mengkondensasi tipe kategori khusus, jika memungkinkan
local ygapdefault = 1 -- asumsi/mulai dari kasus paling umum: 2001, 2002, dll.
local ygap = ygapdefault
if string.match(lastpart, 'presidential') then
local ygap1, ygap2 = ygapdefault, ygapdefault -- perlu menentukan jarak tahun sebelumnya & berikutnya secara independen
local ygap1_success, ygap2_success = false, false
local prevseason = nil
while ygap1 <= ygap_limit do -- Republik Ceko, Polandia, Sri Lanka, dll. memiliki masa jabatan 5 tahun
prevseason = firstpart..lspace..(year - ygap1)..tspace..lastpart
if catexists(prevseason) then
ygap1_success = true
break
end
ygap1 = ygap1 + 1
end
local nextseason = nil
while ygap2 <= ygap_limit do -- Republik Ceko, Polandia, Sri Lanka, dll. memiliki masa jabatan 5 tahun
nextseason = firstpart..lspace..(year + ygap2)..tspace..lastpart
if catexists(nextseason) then
ygap2_success = true
break
end
ygap2 = ygap2 + 1
end
if ygap1_success and ygap2_success then
if ygap1 == ygap2 then ygap = ygap1 end
elseif ygap1_success then
ygap = ygap1
elseif ygap2_success then
ygap = ygap2
end
end
-- lewati tahun yang tidak ada, jika diminta
local ynogaps = {} -- isi dengan tahun yang ada dalam rentang, paling banyak, [year - (skipgaps_limit * 5), year + (skipgaps_limit * 5)]
if skipgaps then
if minyear == minyear_default then
minyear = 0 -- otomatis set minyear ke 0, karena AD/SM tidak didukung
end
if (year > 70) or -- tambahkan dukungan untuk AD/SM (<= M 10) jika diperlukan
(minyear >= 0 and -- harus merupakan seri non-tahun seperti "AC dengan 0 elemen"
not parentM and not parentSM)
then
local yskipped = {} -- lacak y yang dilewati untuk menghindari pengecekan ganda
local cat, found, Yeary
-- isi antrian elemen navigasi ke luar secara positif dari parent
local Year = year -- untuk menyimpan/mengatur progresi
local i = 1
while i <= 5 do
local y = 1
while y <= skipgaps_limit do
found = false
Yeary = Year + y
if yskipped[Yeary] == nil then
yskipped[Yeary] = Yeary
cat = firstpart..lspace..Yeary..tspace..lastpart
found = catexists(cat)
if found then break end
end
y = y + 1
end
if found then Year = Yeary
else Year = Year + 1 end
ynogaps[i] = Year
i = i + 1
end
ynogaps[0] = year -- parent
-- isi antrian elemen navigasi ke luar secara negatif dari parent
Year = year -- reset progresi
i = -1
while i >= -5 do
local y = -1
while y >= -skipgaps_limit do
found = false
Yeary = Year + y
if yskipped[Yeary] == nil then
yskipped[Yeary] = Yeary
cat = firstpart..lspace..Yeary..tspace..lastpart
found = catexists(cat)
if found then break end
end
y = y - 1
end
if found then Year = Yeary
else Year = Year - 1 end
ynogaps[i] = Year
i = i - 1
end
else
skipgaps = false -- TODO: dukungan AD/SM, kemudian angkat pembatasan SM @ [[Template:Establishment category SM]] & [[Template:Year category header/core]]
end
end
-- memulai nav_years
local navy = '<div class="toccolours categorySeriesNavigation-range">\n'
local navlist = {}
local y
local j = 0 -- decrementor untuk kasus khusus "2021 World Rugby Sevens Series" -> "2021–2022"
local i = -5 -- posisi navigasi
while i <= 5 do
if skipgaps then
y = ynogaps[i]
else
y = year + i * ygap * switchMSM - j
end
local SMdisp = ''
if i ~= 0 then -- navigasi kiri/kanan
local M = ''
local SM = ''
if year1to15M and not
(year1to10 and not year1to10SM) -- jangan tampilkan M/SM untuk tahun 1-10 jika parent tidak mengandung M/SM
then
if year >= 11 then -- parent = M 11-15
if y <= 10 then -- tambahkan "M" hanya pada kategori y = 1-10, sesuai kategori yang ada
M = 'M '
end
elseif year >= 1 then -- parent = M 1-10
if y <= 0 then
SM = SMe..' '
y = math.abs(y - 1) -- lewati y = 0 (tidak ada)
elseif y >= 1 and y <= 10 then -- tambahkan "M" hanya pada kategori y = 1-10, sesuai kategori yang ada
M = 'M '
end
end
elseif parentSM then
if switchMSM == -1 then -- y yang ditampilkan berada di rezim SM
if y >= 1 then -- kasus umum
SM = SMe..' '
elseif y == 0 then -- beralih dari SM ke rezim M pada iterasi berikutnya
switchMSM = 1 -- penggunaan y = 0 pertama kali (SM)
end
end
if switchMSM == 1 then -- y yang ditampilkan sekarang berada di rezim M
Y = Y + 1 -- lewati y = 0 (tidak ada)
y = Y -- solusi termudah: mulai iterator lain untuk y M yang ditampilkan pada parent tahun SM
M = 'M '
end
end
if SM ~= '' and year <= 5 then -- hanya tampilkan 'SM' untuk parent tahun <= 5: menghemat ruang, lebih mudah dibaca,
SMdisp = ' '..SMe -- dan 6 adalah tahun navigasi pertama/terakhir yang tidak perlu disambiguasi;
end -- tahun center/parent akan selalu menampilkan SM, jadi tidak perlu menampilkannya lagi
-- mengisi navigasi kiri/kanan
local ysign = y -- gunakan y untuk tampilan & ysign untuk logika
local disp = y..SMdisp
if SM ~= '' then ysign = -ysign end
local firsttry = firstpart..lspace..M..y..tspace..SM..lastpart
if (minyear <= ysign) and (ysign <= maxyear) then
local catlinkM = catlinkfollowr(frame, firsttry, disp ) -- coba M
local catlink = catlinkM -- kandidat awal
if M ~= '' then -- untuk kategori tipe "ACArt dengan 5 elemen yang ditekan"
local catlinkNoM = catlinkfollowr(frame, firstpart..lspace..y..tspace..SM..lastpart, disp ) -- coba tanpa M
if catlinkNoM.catexists == true then
catlink = catlinkNoM -- ganti dengan yang tanpa M
elseif listall then
tlistall[#tlistall] = tlistall[#tlistall]..' (dicoba; tidak ditampilkan)<sup>1</sup>'
end
end
if (M..SM == '') and (catlink.catexists == false) and (y >= 1000) then -- tanpa M/SM & tidak ada; hanya 4 digit, untuk hemat
-- coba kategori dengan tanda hubung dasar: 1-tahun, en-dash, hanya MOS yang benar, tanpa #Rs
local yHyph_4 = y..'–'..(y + 1) -- coba kategori tipe 2010–2011
local catlinkHyph_4 = catlinkfollowr(frame, firstpart..lspace..yHyph_4..tspace..SM..lastpart, yHyph_4 )
if catlinkHyph_4.catexists and catlinkHyph_4.rtarget == nil then -- ada & tanpa #Rs
catlink = catlinkHyph_4 -- ganti dengan yang ini
trackcat(27, 'Navigasi seri kategori tahun dan rentang')
else
if listall then
tlistall[#tlistall] = tlistall[#tlistall]..' (dicoba; tidak ditampilkan)<sup>2</sup>'
end
local yHyph_2 = y..'–'..string.match(y + 1, '%d%d$') -- coba kategori tipe 2010–11
if i == 1 then
local yHyph_2_special = (y - 1)..'–'..string.match(y, '%d%d$') -- coba kasus khusus 2021 -> 2021–22
local catlinkHyph_2_special = catlinkfollowr(frame, firstpart..lspace..yHyph_2_special..tspace..SM..lastpart, yHyph_2_special )
if catlinkHyph_2_special.catexists and catlinkHyph_2_special.rtarget == nil then -- ada & tanpa #Rs
catlink = catlinkHyph_2_special -- ganti dengan yang ini
trackcat(27, 'Navigasi seri kategori tahun dan rentang')
j = 1
elseif listall then
tlistall[#tlistall] = tlistall[#tlistall]..' (dicoba; tidak ditampilkan)<sup>3</sup>'
end
end
if not (i == 1 and j == 1) then
local catlinkHyph_2 = catlinkfollowr(frame, firstpart..lspace..yHyph_2..tspace..SM..lastpart, yHyph_2 )
if catlinkHyph_2.catexists and catlinkHyph_2.rtarget == nil then -- ada & tanpa #Rs
catlink = catlinkHyph_2 -- ganti dengan yang ini
trackcat(27, 'Navigasi seri kategori tahun dan rentang')
elseif listall then
tlistall[#tlistall] = tlistall[#tlistall]..' (dicoba; tidak ditampilkan)<sup>4</sup>'
end
end
end
end
if catlink.rtarget then -- #R diikuti; tentukan alasannya
local r = catlink.rtarget
local c = catlink.cat
local year_regex = '%d%d%d%d[–-]?%d?%d?%d?%d?' -- prioritaskan penghilangan tahun/rentang, mis. "2006 Super 14 season"
local hyph_regex = '%d%d%d%d[–-]%d+' -- lebih ketat
local num_regex = '%d+' -- hapus angka apa pun selain itu
local final_regex = nil -- pilihan terbaik akan ditempatkan di sini
if mw.ustring.match(r, year_regex) and mw.ustring.match(c, year_regex) then
final_regex = year_regex
elseif mw.ustring.match(r, num_regex) and mw.ustring.match(c, num_regex) then
final_regex = num_regex
end
if final_regex then
local r_base = mw.ustring.gsub(r, final_regex, '')
local c_base = mw.ustring.gsub(c, final_regex, '')
if r_base ~= c_base then
trackcat(19, 'Navigasi seri kategori tahun dialihkan (perubahan dasar)') -- target #R yang dapat diterima
elseif mw.ustring.match(r, hyph_regex) then
trackcat(20, 'Navigasi seri kategori tahun dialihkan (perubahan var)') -- mis. "2008 dalam sepak bola wanita Skotlandia" ke "2008–09"
else
trackcat(21, 'Navigasi seri kategori tahun dialihkan (lainnya)') -- pengecualian berada di sini
end
else
trackcat(20, 'Navigasi seri kategori tahun dialihkan (perubahan var)') -- mis. "V2 engines" ke "V-twin engines"
end
end
table.insert(navlist, catlink.navelement)
else -- di luar batas vs min/max
local hidden = '<span style="visibility:hidden">'..disp..'</span>'
table.insert(navlist, hidden)
if listall then
local dummy = catlinkfollowr(frame, firsttry, disp )
tlistall[#tlistall] = tlistall[#tlistall]..' ('..hidden..')'
end
end
else -- navy center
if parentSM then SMdisp = ' '..SMe end
table.insert(navlist, '<b>'..year..SMdisp..'</b>')
end
i = i + 1
end
-- menambahkan daftar
navy = navy..horizontal(navlist)..'\n'
isolatedcat()
if listall then
return listalllinks()
else
return navy..'</div>'
end
end
--[[==========================={{ nav_roman }}==============================]]
local function nav_roman(frame, firstpart, roman, lastpart, minimumrom, maximumrom)
local toarabic = require("Modul:ConvertNumeric").roman_to_numeral
local toroman = require("Modul:Roman").main
--sterilize/convert rom/num
local num = tonumber(toarabic(roman))
local rom = toroman({[1] = num})
if num == nil or rom == nil then --out of range or some other error
errors =
p.errorclass(
'Function nav_roman can\'t recognize one or more of "' ..
(num or "nil") ..
'" & "' ..
(rom or "nil") .. '" dalam kategori "' .. firstpart .. " " .. roman .. " " .. lastpart .. '".'
)
return p.failedcat(errors, "R")
end
--sterilize min/max
local minrom = tonumber(minimumrom or "") or tonumber(toarabic(minimumrom or ""))
local maxrom = tonumber(maximumrom or "") or tonumber(toarabic(maximumrom or ""))
if minrom < 1 then
minrom = 1
end --toarabic() returns -1 on error
if maxrom < 1 then
maxrom = 9999
end --toarabic() returns -1 on error
if minrom > num then
minrom = num
end
if maxrom < num then
maxrom = num
end
--begin navroman
local navr = '{| class="toccolours hlist" style="text-align: center; margin: auto;"\n' .. "|\n"
local i = -5 --nav position
while i <= 5 do
local n = num + i
if n >= 1 then
local r = toroman({[1] = n})
if i ~= 0 then --left/right navr
local catlink = catlinkfollowr(frame, firstpart .. " " .. r .. " " .. lastpart, r)
if minrom <= n and n <= maxrom then
if catlink.rtarget then --a {{Category redirect}} was followed
trackcat(22, "Navseasoncats roman numeral redirected")
end
navr = navr .. "*" .. catlink.navelement .. "\n"
else
local hidden = '<span style="visibility:hidden">' .. r .. "</span>"
navr = navr .. "*" .. hidden .. "\n"
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. " (" .. hidden .. ")"
end
end
else --center navr
navr = navr .. "*<b>" .. r .. "</b>\n"
end
else
navr = navr .. '*<span style="visibility:hidden">' .. "I" .. "</span>\n"
end
i = i + 1
end
isolatedcat()
if listall then
return listalllinks()
else
return navr .. "|}"
end
end
--[[=========================={{ nav_nordinal }}============================]]
local function nav_nordinal(frame, firstpart, ord, lastpart, minimumord, maximumord)
-- Mengonversi ordinal (1st, 2nd, 3rd, dst.) menjadi angka dan mensterilkan nilai minimum/maksimum
local nord = tonumber(ord)
local minord = tonumber(string.match(minimumord or "", "(-?%d+)[snrt]?[tdh]?")) or -9999
local maxord = tonumber(string.match(maximumord or "", "(-?%d+)[snrt]?[tdh]?")) or 9999
-- Mengizinkan penulisan "SM" untuk menunjukkan "Sebelum Masehi" (setara "BC")
if string.match(minimumord or "", "SM") then
minord = -math.abs(minord)
end
if string.match(maximumord or "", "SM") then
maxord = -math.abs(maxord)
end
-- Mendeteksi konteks temporal (misalnya "abad", "milenium") pada lastpart
local temporal = string.match(lastpart, "abad") or string.match(lastpart, "milenium")
-- Spasi di akhir ordinal
local tspace = " "
-- Jika lastpart berawalan "-", tidak perlu spasi (misal "19th-century" -> "19th-century" tanpa spasi)
if string.match(lastpart, "^-") then
tspace = ""
end
----------------------------------------------------------------------------
-- Pengaturan SM/M dan variabel terkait
----------------------------------------------------------------------------
-- Daftar lastpart yang valid untuk SM (dahulu "BC"/"BCE" di versi bahasa Inggris)
local ordSMElastparts = {
["-century Hebrew people"] = "SM",
["-century Jews"] = "SM",
["-century Judaism"] = "SM",
["-century rabbis"] = "SM",
["-century High Priests of Israel"] = "SM"
}
-- parentSM mendeteksi apakah lastpart mengandung " SM" (misal "1st-century SM")
local parentSM = mw.ustring.match(lastpart, "%s(SM?)") -- Mendeteksi " SM" atau " S"
-- lastpartNoSM menghapus " SM" agar lebih mudah diolah
local lastpartNoSM = mw.ustring.gsub(lastpart, "%sSM?", "")
-- BCe dulunya adalah label default "BC", sekarang disesuaikan menjadi "SM"
local SMe = parentSM or ordSMElastparts[lastpartNoSM] or "SM"
-- switchMSM: 1 = M (Masehi), -1 = SM (Sebelum Masehi)
local switchMSM = 1
if parentSM then
switchMSM = -1
end
-- O adalah iterator sekunder apabila kita beralih dari SM ke M
local O = 0
-- Jika konteks bukan "temporal" (bukan abad atau milenium), maka minimal ordinal tidak boleh < 1
if not temporal and minord < 1 then
minord = 1
end
-- Validasi minord & maxord agar tidak melebihi ordinal induk
if minord > nord * switchMSM then
minord = nord * switchMSM
end
if maxord < nord * switchMSM then
maxord = nord * switchMSM
end
----------------------------------------------------------------------------
-- Memulai nav_nordinal
----------------------------------------------------------------------------
local bnb = ""
-- Jika navborder == false, kita hilangkan border agar sesuai untuk beberapa navigasi
if navborder == false then
bnb = " border-style: none; background-color: transparent;"
end
-- Gunakan table HTML untuk meletakkan daftar tautan ordinal
local navo = '{| class="toccolours hlist" style="text-align: center; margin: auto;' .. bnb .. '"\n' ..
'|-\n'
local i = -5 -- posisi navigasi (5 ordinal ke kiri)
while i <= 5 do -- hingga 5 ordinal ke kanan
local o = nord + i * switchMSM
local SM = ""
local SMdisp = ""
-- Jika parentSM terdeteksi, artinya induknya SM
if parentSM then
if switchMSM == -1 then -- Masih di sisi SM
if o >= 1 then
-- Kasus umum SM (misal "3rd-century SM")
SM = " " .. SMe
elseif o == 0 then
-- Beralih dari SM ke M
SM = ""
switchMSM = 1
end
end
if switchMSM == 1 then
-- Sekarang berada di rezim M (Masehi) setelah SM
O = O + 1
o = O
end
else
-- Jika induknya M (Masehi), tetapi ordinal bernilai <= 0, berarti kita menampilkan sisi SM
if o <= 0 then
SM = " " .. SMe
-- Lewati ordinal 0 karena tidak masuk akal ("0th century" dsb.)
o = math.abs(o - 1)
end
end
-- Hanya tampilkan "SM" jika induknya <= 5 untuk menghindari pemakaian label SM yang berlebihan
if SM ~= "" and nord <= 5 then
SMdisp = " " .. SMe
end
-- Bangun ordinal (misal "1st", "2nd" dsb.) lewat pemanggilan `p.addord`
local oth = p.addord(o)
local osign = o
if SM ~= "" then
osign = -osign -- Logika internal untuk membedakan SM
end
-- Elemen tersembunyi jika di luar jangkauan atau jika bernilai 0
local hidden = '<span style="visibility:hidden">' .. oth .. "</span>"
----------------------------------------------------------------------------
-- Penanganan Kategori Temporal (abad, milenium)
----------------------------------------------------------------------------
if temporal then
-- Salin lastpart tanpa SM (agar tidak berulang menambah SM)
local newlastpart = lastpartNoSM
-- Jika kita butuh menambahkan label SM ke "abad" / "milenium"
if SM ~= "" then
newlastpart = string.gsub(newlastpart, temporal, temporal .. SM)
end
local catlink = catlinkfollowr(frame,
firstpart .. " " .. oth .. tspace .. newlastpart,
oth .. SMdisp
)
-- Tampilkan tautan hanya jika berada dalam rentang minord-maxord
if (minord <= osign) and (osign <= maxord) then
if catlink.rtarget then
trackcat(23, "Navseasoncats nordinal dialihkan")
end
navo = navo .. navcenter(i, catlink)
else
navo = navo .. "*" .. hidden .. "\n"
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. " (" .. hidden .. ")"
end
end
----------------------------------------------------------------------------
-- Penanganan Kategori Non-Temporal (misal "1st parliament", dsb.)
----------------------------------------------------------------------------
elseif SM == "" and (minord <= osign) and (osign <= maxord) then
local catlink = catlinkfollowr(frame,
firstpart .. " " .. oth .. tspace .. lastpart,
oth
)
if catlink.rtarget then
trackcat(23, "Navseasoncats nordinal dialihkan")
end
navo = navo .. navcenter(i, catlink)
else
-- Di luar jangkauan, atau penanganan aneh (misal "2nd parliament SM") yang tidak lazim
navo = navo .. "*" .. hidden .. "\n"
end
i = i + 1
end
isolatedcat()
if listall then
return listalllinks()
else
return navo .. "|}"
end
end
--[[========================={{ nav_wordinal }}=============================]]
local function nav_wordinal(frame, firstpart, word, lastpart, minimumword, maximumword, ordinal, frame)
-- Paramater:
-- 1) firstpart : Bagian teks di depan kata bilangan (word).
-- 2) word : Kata bilangan (misalnya "second" atau "two"), masih menggunakan bahasa Inggris.
-- 3) lastpart : Bagian teks setelah kata bilangan.
-- 4) minimumword : Kata bilangan (atau angka) minimal, opsional.
-- 5) maximumword : Kata bilangan (atau angka) maksimal, opsional.
-- 6) ordinal : true untuk menghasilkan bentuk ordinal ('second'), false untuk bentuk kardinal ('two').
-- 7) frame : Objek frame Lua (untuk pemanggilan modul & fungsi bantu lainnya).
-- Dalam Module:ConvertNumeric.spell_number2(), argumen:
-- ordinal == true => 'second' dihasilkan alih-alih 'two'
-- ordinal == false => 'two' dihasilkan alih-alih 'second'
local ord2eng = require('Module:ConvertNumeric').spell_number2
local eng2ord = require('Module:ConvertNumeric').english_to_ordinal
local th = 'th'
-- Jika bukan bentuk ordinal, maka suffix 'th' dihilangkan (karena pakai bentuk kardinal).
if not ordinal then
th = ''
eng2ord = require('Module:ConvertNumeric').english_to_numeral
end
-- Memeriksa huruf kapital (uppercase) pada kata awal (word).
local capitalize = nil ~= string.match(word, '^%u') -- Apakah huruf pertama kapital?
-- Konversi kata bilangan Inggris ke angka (mis. "two" -> 2, "second" -> 2).
local nord = eng2ord(string.lower(word))
-- Mengatur spasi di depan (lspace) dan di belakang (tspace) kata bilangan.
local lspace = ' ' -- Asumsikan ada spasi di depan (kasus umum).
local tspace = ' ' -- Asumsikan ada spasi di belakang (kasus umum).
if string.match(firstpart, '[%-%(]$') then
lspace = '' -- Tanpa spasi, misalnya "Straight-eight engines"-type cats.
end
if string.match(lastpart, '^[%-%)]') then
tspace = '' -- Tanpa spasi, misalnya "Nine-cylinder engines"-type cats.
end
----------------------------------------------------------------------------
-- Mensterilkan nilai minimum / maksimum (minword / maxword).
----------------------------------------------------------------------------
local maxword_default = 99
local maxword = maxword_default
local minword = 1
if minimumword then
local num = tonumber(minimumword)
if num and 0 < num and num < maxword then
minword = num
else
local ord = eng2ord(minimumword)
if 0 < ord and ord < maxword then
minword = ord
end
end
end
if maximumword then
local num = tonumber(maximumword)
if num and 0 < num and num < maxword then
maxword = num
else
local ord = eng2ord(maximumword)
if 0 < ord and ord < maxword then
maxword = ord
end
end
end
-- Pastikan minword <= nord <= maxword agar sesuai dengan kategori induk.
if minword > nord then
minword = nord
end
if maxword < nord then
maxword = nord
end
----------------------------------------------------------------------------
-- Menentukan kategori terakhir (n_max) yang benar-benar ada (catexists).
-- Logika ini memeriksa hingga 5 kata bilangan setelah (nord + m).
----------------------------------------------------------------------------
local listoverride = true
local n_max = nord
local m = 1
while m <= 5 do
local n = nord + m
local nth = p.addord(n)
if not ordinal then
nth = n -- Jika bukan ordinal, pakai angka biasa.
end
-- Konversi balik ke kata bilangan Inggris (mis. 2 -> "two" / "second").
local w = ord2eng{
num = n,
ordinal = ordinal,
capitalize = capitalize
}
local catlink = catlinkfollowr(
frame,
firstpart .. lspace .. w .. tspace .. lastpart,
nth,
nil,
listoverride
)
-- Jika kategori ada, perbarui n_max
if catlink.catexists then
n_max = n
end
m = m + 1
end
----------------------------------------------------------------------------
-- Memulai nav_wordinal (navigasi kata bilangan).
----------------------------------------------------------------------------
local navw = '<div class="toccolours categorySeriesNavigation-range">\n'
local navlist = {}
local prepad = ''
local i = -5 -- Menampilkan 5 kata bilangan sebelum induk
while i <= 5 do -- Hingga 5 kata bilangan setelah induk
local n = nord + i
if n >= 1 then
local nth = p.addord(n)
if not ordinal then
nth = n
end
if i ~= 0 then
-- Bagian kiri/kanan dari navigasi
local w = ord2eng{
num = n,
ordinal = ordinal,
capitalize = capitalize
}
local catlink = catlinkfollowr(
frame,
firstpart .. lspace .. w .. tspace .. lastpart,
nth
)
if minword <= n and n <= maxword then
if catlink.rtarget then
-- Sebuah {{Category redirect}} diikuti
trackcat(24, 'Navigasi seri kategori wordinal dialihkan')
end
-- Tampilkan normal jika masih dalam jangkauan n_max atau maxword bukan default
if n <= n_max or maxword ~= maxword_default then
table.insert(navlist, prepad .. catlink.navelement)
prepad = ''
else
-- Jika melebihi n_max, tampilkan sebagai teks tak terlihat
local postpad = '<span style="visibility:hidden"> • ' .. nth .. '</span>'
navlist[#navlist] = (navlist[#navlist] or '') .. postpad
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. ' (' .. postpad .. ')'
end
end
else
-- Di luar rentang minword / maxword
local postpad = '<span style="visibility:hidden"> • ' .. nth .. '</span>'
navlist[#navlist] = (navlist[#navlist] or '') .. postpad
if listall then
tlistall[#tlistall] = tlistall[#tlistall] .. ' (' .. postpad .. ')'
end
end
else
-- Bagian tengah (center) dari navigasi (kata bilangan induk)
table.insert(navlist, prepad .. '<b>' .. nth .. '</b>')
prepad = ''
end
else
-- Jika n < 1, tampilkan placeholder agar tata letak tetap rapi
prepad = prepad .. '<span style="visibility:hidden"> • ' .. '0' .. th .. '</span>'
if listall then
tlistall[#tlistall] = (tlistall[#tlistall] or '') .. ' (x)'
end
end
i = i + 1
end
-- Menambahkan daftar ke string hasil
navw = navw .. horizontal(navlist) .. '\n'
isolatedcat()
if listall then
return listalllinks()
else
return navw .. '</div>'
end
end
--[[==========================={{ find_var }}===============================]]
local function find_var( pn )
-- Mengekstrak teks variabel (misalnya tahun, musim, dll.) dari sebuah string,
-- dan mengembalikan { ['vtype'] = <'tahun'|'musim'|dll.>, <v> = <2023|2023–24|dll.> }
local pagename = currtitle.text
if pn and pn ~= '' then
pagename = pn
end
local cpagename = 'Kategori:'..pagename -- workaround regex Lua terbatas
-- Pola untuk dekade, misalnya "1990-an di Indonesia (1990–1999)"
local d_season = mw.ustring.match(cpagename, ':(%d-an).+%(%d+[–-]%d+%)')
-- Pola untuk tahun dengan penambahan, misalnya "Pendiri tahun 2020 di Jakarta (2020–2024)"
local y_season = mw.ustring.match(cpagename, ':(%d+) .+%(%d+[–-]%d+%)')
-- Pola untuk tahun yang belum selesai, misalnya "Anggota Parlemen Indonesia 2021–"
local e_season = mw.ustring.match(cpagename, '%s(%d+[–-])$') or
mw.ustring.match(cpagename, '%s(%d+[–-]sekarang)$')
-- Pola untuk musim atau periode, misalnya "Periode 2023–2024"
local season = mw.ustring.match(cpagename, '[:%s%(](%d+[–-]%d+)[%)%s]') or
mw.ustring.match(cpagename, '[:%s](%d+[–-]%d+)$')
-- Pola untuk musim TV, misalnya "musim 3"
local tvseason = mw.ustring.match(cpagename, 'musim (%d+)') or
mw.ustring.match(cpagename, 'seri (%d+)')
-- Pola untuk ordinal dalam Bahasa Indonesia, misalnya "ke-3"
local nordinal = mw.ustring.match(cpagename, '[:%s](ke%-%d+)[%s-]') or
mw.ustring.match(cpagename, '[:%s](ke%-%d+)$')
-- Pola untuk dekade, misalnya "1990-an"
local decade = mw.ustring.match(cpagename, '[:%s](%d+%-an)[%s-]') or
mw.ustring.match(cpagename, '[:%s](%d+%-an)$')
-- Pola untuk tahun, prioritas pada tahun 4 digit
local year = mw.ustring.match(cpagename, '[:%s](%d%d%d%d)%s') or
mw.ustring.match(cpagename, '[:%s](%d%d%d%d)$') or
mw.ustring.match(cpagename, '[:%s](%d+)%s') or
mw.ustring.match(cpagename, '[:%s](%d+)$') or
-- Ekspansi/kombinasi pengecualian jika diperlukan
mw.ustring.match(cpagename, '[:%s](%d+)-terkait') or
mw.ustring.match(cpagename, '[:%s](%d+)-silinder') or
mw.ustring.match(cpagename, '[:%-VW](%d+)%s') -- misalnya "Mesin Straight-8"
-- Pola untuk angka Romawi, misalnya "III"
local roman = mw.ustring.match(cpagename, '%s([IVXLCDM]+)%s')
-- Menemukan variabel yang cocok
local found = d_season or y_season or e_season or season or tvseason or
nordinal or decade or year or roman
if found then
if string.match(found, '%d%d%d%d%d') == nil then
-- Mengembalikan berdasarkan kompleksitas/kemungkinan duplikasi
if nordinal and season then -- misalnya "Pendiri abad ke-18 di Indonesia (1763–1791)"
return { ['vtype'] = 'nordinal', ['v'] = nordinal }
end
if d_season then return { ['vtype'] = 'decade', ['v'] = d_season } end
if y_season then return { ['vtype'] = 'year', ['v'] = y_season } end
if e_season then return { ['vtype'] = 'ending', ['v'] = e_season } end
if season then return { ['vtype'] = 'season', ['v'] = season } end
if tvseason then return { ['vtype'] = 'tvseason', ['v'] = tvseason } end
if nordinal then return { ['vtype'] = 'nordinal', ['v'] = nordinal } end
if decade then return { ['vtype'] = 'decade', ['v'] = decade } end
if year then return { ['vtype'] = 'year', ['v'] = year } end
if roman then return { ['vtype'] = 'roman', ['v'] = roman } end
end
else
-- Mencoba ordinal dalam Bahasa Inggris ('zeroth' hingga 'ninety-ninth' saja)
local eng2ord = require('Module:ConvertNumeric').indonesian_to_ordinal
local split = mw.text.split(pagename, ' ')
for i=1, #split do
if eng2ord(split[i]) > -1 then
return { ['vtype'] = 'wordinal', ['v'] = split[i] }
end
end
-- Mencoba numerik dalam Bahasa Inggris ('one'/'single' hingga 'ninety-nine' saja)
local eng2num = require('Module:ConvertNumeric').indonesian_to_numeral
local split = mw.text.split(pagename, '[%s%-]') -- misalnya "Mesin-silinder sembilan"
for i=1, #split do
if eng2num(split[i]) > -1 then
return { ['vtype'] = 'enumeric', ['v'] = split[i] }
end
end
end
-- Jika tidak ditemukan, kembalikan error
errors = p.errorclass('Fungsi find_var tidak dapat menemukan teks variabel dalam kategori "'..pagename..'".')
return { ['vtype'] = 'error', ['v'] = p.failedcat(errors, 'V') }
end
--[[==========================================================================]]
--[[ Main ]]
--[[==========================================================================]]
function p.navseasoncats(frame)
--arg checks & handling
local args = frame:getParent().args
checkforunknownparams(args) --for template args
checkforunknownparams(frame.args) --for #invoke'd args
local cat = args["cat"] --'testcase' alias for catspace
local list = args["list-all-links"] --debugging utility to output all links & followed #Rs
local follow = args["follow-redirects"] --default 'yes'
local testcase = args["testcase"]
local testcasegap = args["testcasegap"]
local minimum = args["min"]
local maximum = args["max"]
local skip_gaps = args["skip-gaps"]
--apply args
local pagename = testcase or cat or currtitle.text
local testcaseindent = ""
if testcasecolon == ":" then
testcaseindent = "\n::"
end
if follow and follow == "no" then
followRs = false
end
if list and list == "yes" then
listall = true
end
if skip_gaps and skip_gaps == "yes" then
skipgaps = true
trackcat(26, "Navseasoncats dengan parameter skip-gaps")
end
--ns checks
if currtitle.nsText == "Kategori" then
if cat and cat ~= "" then
trackcat(1, "Navseasoncats using cat parameter")
end
if testcase and testcase ~= "" then
trackcat(2, "Navseasoncats using testcase parameter")
end
elseif currtitle.nsText == "" then
trackcat(30, "Navseasoncats in mainspace")
end
--find the variable parts of pagename
local findvar = find_var(pagename)
if findvar.vtype == "error" then --basic format error checking in find_var()
return findvar.v .. table.concat(ttrackingcats)
end
local start = string.match(findvar.v, "^%d+")
--the rest is static
local findvar_escaped = string.gsub(findvar.v, "%-", "%%%-")
local firstpart, lastpart = string.match(pagename, "^(.-)" .. findvar_escaped .. "(.*)$")
if findvar.vtype == "tvseason" then --double check for cases like '30 Rock (season 3) episodes'
firstpart, lastpart = string.match(pagename, "^(.-musim )" .. findvar_escaped .. "(.*)$")
if firstpart == nil then
firstpart, lastpart = string.match(pagename, "^(.-seri )" .. findvar_escaped .. "(.*)$")
end
end
firstpart = mw.text.trim(firstpart or "")
lastpart = mw.text.trim(lastpart or "")
--call the appropriate nav function, in order of decreasing popularity
if findvar.vtype == "year" then --e.g. "500", "2001"; nav_year..nav_decade; ~75% of cats
local nav1 =
nav_year(frame, firstpart, start, lastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
local dec = math.floor(findvar.v / 10)
local decadecat = nil
local firstpart_dec = firstpart
if firstpart_dec ~= "" then
firstpart_dec = firstpart_dec .. " "
elseif firstpart_dec == "M" and dec <= 1 then
firstpart_dec = ""
if dec == 0 then
dec = ""
end
end
local decade = dec .. "0-an "
decadecat = mw.text.trim(firstpart_dec .. " " .. decade .. lastpart)
local exists = catexists(decadecat)
if exists then
navborder = false
trackcat(28, "Navseasoncats year and decade")
local nav2 =
nav_decade(frame, firstpart_dec, decade, lastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
return nav1nav2(nav1, nav2)
elseif ttrackingcats[16] ~= "" then --nav_year isolated; check nav_hyphen (e.g. UK MPs 1974, Moldovan MPs 2009, etc.)
local hyphen = "–"
local finish = start
local nav2 =
nav_hyphen(frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap) ..
testcaseindent .. table.concat(ttrackingcats)
if ttrackingcats[16] ~= "" then
return nav1 --still isolated; rv to nav_year
else
return nav2
end
else --regular nav_year
return nav1
end
elseif findvar.vtype == "decade" then --e.g. "0s", "2010s"; nav_decade..nav_nordinal; ~12% of cats
local nav1 =
nav_decade(frame, firstpart, start, lastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
local decade = tonumber(string.match(findvar.v, "^(%d+)%-an"))
local century = math.floor(((decade - 1) / 100) + 1) --from {{CENTURY}}
if century == 0 then
century = 1
end --no 0th century
if string.match(decade, "00$") then
century = century + 1 --'2000' is in the 20th, but the rest of the 2000s is in the 21st
end
local clastpart = " abad " .. lastpart
local centurycat = mw.text.trim(firstpart .. " " .. p.addord(century) .. clastpart)
local exists = catexists(centurycat)
if not exists then --check for hyphenated century
clastpart = "-abad " .. lastpart
centurycat = mw.text.trim(firstpart .. " " .. p.addord(century) .. clastpart)
exists = catexists(centurycat)
end
if exists then
navborder = false
trackcat(29, "Navseasoncats decade and century")
local nav2 =
nav_nordinal(frame, firstpart, century, clastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
return nav1nav2(nav1, nav2)
else
return nav1
end
elseif findvar.vtype == "nordinal" then --e.g. "1st", "99th"; ~7.5% of cats
return nav_nordinal(frame, firstpart, start, lastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
elseif findvar.vtype == "season" then --e.g. "1–4", "1999–2000", "2001–02", "2001–2002", "2005–2010", etc.; ~5.25%
local hyphen, finish = mw.ustring.match(findvar.v, "%d([–-])(%d+)") --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd
return nav_hyphen(frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap) ..
testcaseindent .. table.concat(ttrackingcats)
elseif findvar.vtype == "tvseason" then --e.g. "1", "15" but preceded with "season" or "series"; <1% of cats
return nav_tvseason(frame, firstpart, start, lastpart, maximum) .. testcaseindent .. table.concat(ttrackingcats) --"minimum" defaults to 1
elseif findvar.vtype == "wordinal" then --e.g. "first", "ninety-ninth"; <<1% of cats
local ordinal = "on"
return nav_wordinal(frame, firstpart, findvar.v, lastpart, minimum, maximum, ordinal, frame) ..
testcaseindent .. table.concat(ttrackingcats)
elseif findvar.vtype == "enumeric" then --e.g. "one", "ninety-nine"; <<1% of cats
local ordinal = "off"
return nav_wordinal(frame, firstpart, findvar.v, lastpart, minimum, maximum, ordinal, frame) ..
testcaseindent .. table.concat(ttrackingcats)
elseif findvar.vtype == "roman" then --e.g. "I", "XXVIII"; <<1% of cats
return nav_roman(frame, firstpart, findvar.v, lastpart, minimum, maximum) ..
testcaseindent .. table.concat(ttrackingcats)
elseif findvar.vtype == "ending" then --e.g. "2021–" (irregular; ending unknown); <<<1% of cats
local hyphen, finish = mw.ustring.match(findvar.v, "%d([–-])sekarang$"), -1 --ascii 150 & 45 (ndash & keyboard hyphen); mw req'd
if hyphen == nil then
hyphen, finish = mw.ustring.match(findvar.v, "%d([–-])$"), 0 --0/-1 are hardcoded switches for nav_hyphen()
end
return nav_hyphen(frame, start, hyphen, finish, firstpart, lastpart, minimum, maximum, testcasegap) ..
testcaseindent .. table.concat(ttrackingcats)
else --malformed
errors =
p.errorclass(
'Failed to determine the appropriate nav function from malformed season "' .. findvar.v .. '". '
)
return p.failedcat(errors, "N") .. table.concat(ttrackingcats)
end
end
return p