Wikibuku
minwikibooks
https://min.wikibooks.org/wiki/Palanta
MediaWiki 1.47.0-wmf.20
first-letter
Media
Istimewa
Rundiang
Pangguno
Rundiang Pangguno
Wikibuku
Rundiang Wikibuku
Berkas
Rundiang Berkas
MediaWiki
Rundiang MediaWiki
Templat
Rundiang Templat
Bantuan
Rundiang Bantuan
Kategori
Rundiang Kategori
TimedText
TimedText talk
Modul
Rundiang Modul
Acara
Pembicaraan Acara
Templat:Dokumentasi
10
1598
10380
7608
2026-09-16T08:13:33Z
Zhilal Darma
23
←Mengganti halaman dengan '{{#invoke:documentation|show|hr=above}}'
10380
wikitext
text/x-wiki
{{#invoke:documentation|show|hr=above}}
j0s13scsii7qihzcaj44tsxynimxx68
10383
10380
2026-09-16T08:23:03Z
Zhilal Darma
23
10383
wikitext
text/x-wiki
{{#invoke:Documentation|show|hr=above}}
fhrxjlmu4cy8shbgrr9nox4pd3v8l49
10387
10383
2026-09-16T08:27:14Z
Zhilal Darma
23
10387
wikitext
text/x-wiki
{{#invoke:documentation|show|hr=above}}
j0s13scsii7qihzcaj44tsxynimxx68
Templat:Wikilatih
10
2531
10376
10371
2026-09-16T08:01:17Z
Zhilal Darma
23
10376
wikitext
text/x-wiki
<div style="float: right">{{userbox|white|lightgray|[[File:{{{logo|Wikimedia-logo-id.svg}}}|45px|link=]]|Pangguno iko ikuik sato dalam '''[[:wmid:WikiLatih|#WikiLatih]]''' {{{proyek|manyuntiang Wikikato }}} {{#if:{{{acara|}}}| untuak acara {{{acara}}}}}{{#if:{{{tgl|}}}| pado tangga {{{tgl}}}}}{{#if:{{{di|}}}| di {{{di}}}}}.}}<includeonly>[[Category:Pangguno nan ikuik WikiLatih{{#if:{{{tgl|}}}| tangga {{{tgl}}}}}|{{PAGENAME}}]]</includeonly></div>
<noinclude>{{dokumentasi}}</noinclude>
7mi7qe0y695bkq0rmacsyx05ksxw5y3
10377
10376
2026-09-16T08:03:04Z
Zhilal Darma
23
10377
wikitext
text/x-wiki
<div style="float: right">{{userbox|white|lightgray|[[File:{{{logo|Wikimedia-logo-id.svg}}}|45px|link=]]|Pangguno iko ikuik sato dalam '''[[:wmid:WikiLatih|#WikiLatih]]''' {{{proyek|manyuntiang Wikibuku }}} {{#if:{{{acara|}}}| untuak acara {{{acara}}}}}{{#if:{{{tgl|}}}| pado tangga {{{tgl}}}}}{{#if:{{{di|}}}| di {{{di}}}}}.}}<includeonly>[[Category:Pangguno nan ikuik WikiLatih{{#if:{{{tgl|}}}| tangga {{{tgl}}}}}|{{PAGENAME}}]]</includeonly></div>
<noinclude>{{dokumentasi}}</noinclude>
i1u8ml2vrof64l7tnsy7lqltzxu9bxx
10378
10377
2026-09-16T08:08:22Z
Zhilal Darma
23
10378
wikitext
text/x-wiki
<div style="float: right">
{{userbox|white|lightgray|[[File:{{{logo|Wikimedia-logo-id.svg}}}|45px|link=]]|Pangguno iko ikuik sato dalam '''[[:wmid:WikiLatih|#WikiLatih]]''' {{{proyek|manyuntiang Wikibuku }}} {{#if:{{{acara|}}}| untuak acara {{{acara}}}}}{{#if:{{{tgl|}}}| pado tangga {{{tgl}}}}}{{#if:{{{di|}}}| di {{{di}}}}}.}}<includeonly>[[Category:Pangguno nan ikuik WikiLatih{{#if:{{{tgl|}}}| tangga {{{tgl}}}}}|{{PAGENAME}}]]</includeonly></div>
<noinclude>{{Wikilatih/dokumentasi}}</noinclude>
rggt6sa4hyb9ibwf9pu69p0ktbn958e
10379
10378
2026-09-16T08:10:02Z
Zhilal Darma
23
10379
wikitext
text/x-wiki
<div style="float: right">
{{userbox|white|lightgray|[[File:{{{logo|Wikimedia-logo-id.svg}}}|45px|link=]]|Pangguno iko ikuik sato dalam '''[[:wmid:WikiLatih|#WikiLatih]]''' {{{proyek|manyuntiang Wikibuku }}} {{#if:{{{acara|}}}| untuak acara {{{acara}}}}}{{#if:{{{tgl|}}}| pado tangga {{{tgl}}}}}{{#if:{{{di|}}}| di {{{di}}}}}.}}<includeonly>[[Category:Pangguno nan ikuik WikiLatih{{#if:{{{tgl|}}}| tangga {{{tgl}}}}}|{{PAGENAME}}]]</includeonly></div>
<noinclude>{{dokumentasi}}</noinclude>
efbkakbjnqurv19hrjozpog525dr7ag
10391
10379
2026-09-16T08:36:25Z
Zhilal Darma
23
10391
wikitext
text/x-wiki
<div style="float: right">
{{userbox|white|lightgray|[[File:{{{logo|Wikimedia-logo-id.svg}}}|45px|link=]]|Pangguno iko ikuik sato dalam '''[[:wmid:WikiLatih|#WikiLatih]]''' {{{proyek|manyuntiang Wikibuku }}} {{#if:{{{acara|}}}| untuak acara {{{acara}}}}}{{#if:{{{tgl|}}}| pado tangga {{{tgl}}}}}{{#if:{{{di|}}}| di {{{di}}}}}.}}<includeonly>[[Category:Pangguno nan ikuik WikiLatih{{#if:{{{tgl|}}}| tangga {{{tgl}}}}}|{{PAGENAME}}]]</includeonly></div>
19gff6yw7yocamx4qfw92uqrkz6lm7v
Modul:Documentation
828
2547
10381
2026-09-16T08:21:35Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local pages_module = "Module:pages" local m_pages = require(pages_module) local get_pagetype = m_pages.get_pagetype local is_documentation = m_pages.is_documentation local is_sandbox = m_pages.is_sandbox -- it is either here, or in [[Module:ugly hacks]], and it is not in ugly hacks. function export.CONTENTMODEL() return mw.title.getCurrentTitle().contentModel end local skins = { ["common" ] = ""; ["vector" ] = "Vector"; ["monobook"..."
10381
Scribunto
text/plain
local export = {}
local pages_module = "Module:pages"
local m_pages = require(pages_module)
local get_pagetype = m_pages.get_pagetype
local is_documentation = m_pages.is_documentation
local is_sandbox = m_pages.is_sandbox
-- it is either here, or in [[Module:ugly hacks]], and it is not in ugly hacks.
function export.CONTENTMODEL()
return mw.title.getCurrentTitle().contentModel
end
local skins = {
["common" ] = "";
["vector" ] = "Vector";
["monobook" ] = "Monobook";
["cologneblue"] = "Cologne Blue";
["modern" ] = "Modern";
}
local Array = require "Module:array"
local function track(page)
require("Module:debug/track")("documentation/" .. page)
return true
end
local function compare_pages(page1, page2, text)
return "[" .. tostring(
mw.uri.fullUrl("Special:ComparePages", {page1 = page1, page2 = page2}))
.. " " .. text .. "]"
end
local function page_exists(title)
local success, title_obj = pcall(mw.title.new, title)
return success and title_obj.exists
end
-- Avoid transcluding [[Module:languages/cache]] everywhere.
local lang_cache = setmetatable({}, { __index = function (self, k)
return require "Module:languages/cache"[k]
end })
local function zh_link(word)
return require("Module:links").full_link{
lang = lang_cache.zh,
term = word
}
end
local function make_languages_data_documentation(title, cats, division)
local doc_template, module_cat
if division:find("/extra$") then
division = division:gsub("/extra$", "")
doc_template = "language extradata documentation"
module_cat = "Language extra data modules"
else
doc_template = "language data documentation"
module_cat = "Language data modules"
end
local sort_key
if division == "exceptional" then
sort_key = "x"
else
sort_key = division:gsub("/", "")
end
cats:insert(module_cat .. "|" .. sort_key)
return {
title = doc_template
}
end
local function make_Unicode_data_documentation(title, cats)
local subpage, first_three_of_code_point
= title.fullText:match("^Module:Unicode data/([^/]+)/(%x%x%x)$")
if subpage == "names" or subpage == "images" or subpage == "emoji images" then
local low, high =
tonumber(first_three_of_code_point .. "000", 16),
tonumber(first_three_of_code_point .. "FFF", 16)
local text, text_type
if subpage == "names" then
text_type = "titles of images"
elseif subpage == "images" then
text_type = "titles of images"
elseif subpage == "emoji images" then
text_type = "emoji-style images"
end
text = string.format(
"This data module contains the " .. text_type .. " of " ..
"[[Appendix:Unicode|Unicode]] code points within the range U+%04X to U+%04X.",
low, high)
if subpage == "images" and pcall(mw.loadData, "Module:Unicode data/emoji images/" .. first_three_of_code_point) then
text = text .. " This list includes the text variants of emojis. For the list of emoji variants of those characters, see [[Module:Unicode data/emoji images/" .. first_three_of_code_point .. "]]."
elseif subpage == "emoji images" then
text = text .. " For text-style images, see [[Module:Unicode data/images/" .. first_three_of_code_point .. "]]."
end
return text
end
end
local function insert_lang_data_module_cats(cats, langcode, overall_data_module_cat)
local lang = lang_cache[langcode]
if lang then
local langname = lang:getCanonicalName()
cats:insert(overall_data_module_cat .. "|" .. langname)
cats:insert(langname .. " modules")
cats:insert(langname .. " data modules")
return lang, langname
end
end
--[=[
This provides categories and documentation for various data modules, so that [[Category:Uncategorized modules]] isn't
unnecessarily cluttered. It is a list of tables, each of which have the following possible fields:
`regex` (required): A Lua pattern to match the module's title. If it matches, the data in this entry will be used.
Any captures in the pattern can by referenced in the `cat` field using %1 for the first capture, %2 for the
second, etc. (often used for creating the sortkey for the category). In addition, the captures are passed to the
`process` function as the third and subsequent parameters.
`process` (optional): This may be a function or a string. If it is a function, it is called as follows:
`process(TITLE, CATS, CAPTURE1, CAPTURE2, ...)`
where:
* TITLE is a title object describing the module's title; see
[https://www.mediawiki.org/wiki/Extension:Scribunto/Lua_reference_manual#Title_objects].
* CATS is an array object (see [[Module:array]]) of categories that the module will be added to.
* CAPTURE1, CAPTURE2, ... contain any captures in the `regex` field.
The return value of `process` should either be a string (which will be used as the module's documentation), or a
table specifying the name of a template to expand to get the documentation, along with the arguments to that
template. In the latter format, the template name (bare, without the "Template:" prefix) should be in the `title`
field, and any arguments should be in `args; in this case, the template name will be listed above the generated
documentation as the source of the documentation, along with an edit button to edit the template's contents.
If, however, the return value of the `process` function is a string, any template invocations will be expanded
using frame:preprocess(), and [[Module:documentation]] will be listed as the source of the documentation.
If `process` itself is a string rather than a function, it should name a submodule under
[[Module:documentation/functions/]] which returns a function, of the same type as described above. This submodule
will be specified as the source of the documentation (unless it returns a table naming a template to expand to get
the documentation, as described above).
If `process` is omitted entirely, the module will have no documentation.
`cat` (optional): A string naming the category into which the module should be placed, or a list of such strings.
Captures specified in `regex` may be referenced in this string using %1 for the first capture, %2 for the second,
etc. It is also possible to add categories in the `process` function by inserting them into the passed-in CATS
array (the second parameter).
]=]
local module_regex = {
{
regex = "^Module:languages/data/(3/[a-z]/extra)$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/data/(3/[a-z])$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/data/(2/extra)$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/data/(2)$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/data/(exceptional/extra)$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/data/(exceptional)$",
process = make_languages_data_documentation,
},
{
regex = "^Module:languages/.+$",
cat = "Language and script modules",
},
{
regex = "^Module:scripts/.+$",
cat = "Language and script modules",
},
{
regex = "^Module:data tables/data..?.?.?$",
cat = "Reference module sharded data tables",
},
{
regex = "^Module:zh/data/dial%-pron/.+$",
cat = "Chinese dialectal pronunciation data modules",
process = "zh dial or syn",
},
{
regex = "^Module:zh/data/dial%-syn/.+$",
cat = "Chinese dialect synonyms data modules",
process = "zh dial or syn",
},
{
regex = "^Module:zh/data/glyph%-data/.+$",
cat = "Chinese historical character forms data modules",
process = function(title, cats)
local character = title.fullText:match("^Module:zh/data/glyph%-data/(.+)")
if character then
return ("This module contains data on historical forms of the Chinese character %s.")
:format(zh_link(character))
end
end,
},
{
regex = "^Module:zh/data/ltc%-pron/(.+)$",
cat = "Middle Chinese pronunciation data modules|%1",
process = "zh data",
},
{
regex = "^Module:zh/data/och%-pron%-BS/(.+)$",
cat = "Old Chinese (Baxter-Sagart) pronunciation data modules|%1",
process = "zh data",
},
{
regex = "^Module:zh/data/och%-pron%-ZS/(.+)$",
cat = "Old Chinese (Zhengzhang) pronunciation data modules|%1",
process = "zh data",
},
{
-- capture rest of zh/data submodules
regex = "^Module:zh/data/(.+)$",
cat = "Chinese data modules|%1",
},
{
regex = "^Module:mul/guoxue%-data/cjk%-?(.*)$",
process = "guoxue-data",
},
{
regex = "^Module:Unicode data/(.+)$",
cat = "Unicode data modules|%1",
process = make_Unicode_data_documentation,
},
{
regex = "^Module:number list/data/(.+)$",
process = function(title, cats, lang_code)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Number data modules")
if lang then
return ("This module contains data on various types of numbers in %s.\n%s")
:format(lang:makeCategoryLink(), require("Module:number list/show").table() or "")
end
end,
},
{
regex = "^Module:accel/(.+)$",
process = function(title, cats)
local lang_code = title.subpageText
local lang = lang_cache[lang_code]
if lang then
cats:insert(lang:getCanonicalName() .. " modules|accel")
cats:insert(("Accel submodules|%s"):format(lang:getCanonicalName()))
return ("This module contains new entry creation rules for %s; see [[WT:ACCEL]] for an overview, and [[Module:accel]] for information on creating new rules.")
:format(lang:makeCategoryLink())
end
end,
},
{
regex = "^Module:inc%-ash/dial/data/(.+)$",
cat = "Ashokan Prakrit modules|%1",
process = function(title, cats)
local word = title.fullText:match("^Module:inc%-ash/dial/data/(.+)$")
if word then
local lang = lang_cache["inc-ash"]
return ("This module contains data on the pronunciation of %s in dialects of %s.")
:format(require("Module:links").full_link({ term = word, lang = lang }, "term"),
lang:makeCategoryLink())
end
end,
},
{
regex = "^.+%-translit$",
process = "translit",
},
{
regex = "^Module:form of/lang%-data/(.+)$",
process = function(title, cats, lang_code)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Language-specific form-of modules")
if lang then
-- FIXME, display more info.
return "This module contains language-specific form-of data (tags, shortcuts, base lemma params. etc.) for " ..
langname .. "."
end
end
},
{
regex = "^Module:labels/data/lang/(.+)$",
process = function(title, cats, lang_code)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Language-specific label data modules")
if lang then
return {
title = "label language-specific data documentation",
args = { [1] = lang_code },
}
end
end
},
{
regex = "^Module:category tree/poscatboiler/data/lang%-specific/(.+)$",
process = function(title, cats, lang_code)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Category tree data modules/poscatboiler")
if lang then
return "This module handles generating the descriptions and categorization for " .. langname .. " category pages "
.. "of the format \"" .. langname .. " LABEL\" where LABEL can be any text. Examples are "
.. "[[:Category:Bulgarian conjugation 2.1 verbs]] and [[:Category:Russian velar-stem neuter-form nouns]]. "
.. "This module is part of the poscatboiler system, which is a general framework for generating the "
.. "descriptions and categorization of category pages.\n\n"
.. "For more information, see [[Module:category tree/poscatboiler/data/lang-specific/documentation]].\n\n"
.. "'''NOTE:''' If you add a new language-specific module, you must add the language code to the "
.. "list at the top of [[Module:category tree/poscatboiler/data/lang-specific]] in order for the module to be "
.. "recognized."
end
end
},
{
regex = "^Module:category tree/poscatboiler/data/(.+)$",
process = function(title, cats, submodule)
cats:insert("Category tree data modules/poscatboiler| ")
return {
title = "poscatboiler data submodule documentation"
}
end
},
{
regex = "^Module:category tree/topic cat/data/(.+)$",
process = function(title, cats, submodule)
cats:insert("Category tree data modules/topic cat| ")
return {
title = "topic cat data submodule documentation"
}
end
},
{
regex = "^Module:ja/data/(.+)$",
cat = "Japanese data modules|%1",
},
{
regex = "^Module:fi%-dialects/data/feature/Kettunen1940 ([0-9]+)$",
cat = "Finnish dialectal data atlas modules|%1",
process = function(title, cats, shard)
return "This module contains shard " .. shard .. " of the online version of Lauri Kettunen's 1940 work " ..
"''Suomen murteet III A. Murrekartasto'' (\"Finnish dialects III A: Dialect atlas\"). " ..
"It was imported and converted from urn:nbn:fi:csc-kata20151130145346403821, published by the " ..
"''Kotimaisten kielten keskus'' under the CC BY 4.0 license."
end
},
{
regex = "^Module:fi%-dialects/data/feature/(.+)",
cat = "Finnish dialectal data modules|%1",
},
{
regex = "^Module:fi%-dialects/data/word/(.+)",
cat = "Finnish dialectal data modules|%1",
},
{
regex = "^Module:Swadesh/data/([a-z-]+)$",
process = function(title, cats, lang_code)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Swadesh modules")
if lang then
return "This module contains the [[Swadesh list]] of basic vocabulary in " .. langname .. "."
end
end
},
{
regex = "^Module:Swadesh/data/([a-z-]+)/([^/]*)$",
process = function(title, cats, lang_code, variety)
local lang, langname = insert_lang_data_module_cats(cats, lang_code, "Swadesh modules")
if lang then
local prefix = "This module contains the [[Swadesh list]] of basic vocabulary in the "
local etym_lang = require("Module:languages").getByCode(variety, nil, "allow etym")
if etym_lang then
return ("%s %s variety of %s."):format(prefix, etym_lang:getCanonicalName(), langname)
end
local script = require("Module:scripts").getByCode(variety)
if script then
return ("%s %s %s script."):format(prefix, langname, script:getCanonicalName())
end
return ("%s %s variety of %s."):format(prefix, variety, langname)
end
end
},
{
regex = "^Module:typing%-aids",
process = function(title, cats)
local data_suffix = title.fullText:match("^Module:typing%-aids/data/(.+)$")
local sortkey
if data_suffix then
if data_suffix:find "^[%l-]+$" then
local lang = require("Module:languages").getByCode(data_suffix)
if lang then
sortkey = lang:getCanonicalName()
cats:insert(sortkey .. " data modules")
end
elseif data_suffix:find "^%u%l%l%l$" then
local script = require("Module:scripts").getByCode(data_suffix)
if script then
sortkey = script:getCanonicalName()
cats:insert(script:getCategoryName())
end
end
cats:insert("Character insertion data modules|" .. (sortkey or data_suffix))
end
end,
},
{
regex = "^Module:R:([a-z%-]+):(.+)$",
process = function(title, cats, lang_code, refname)
local lang = lang_cache[lang_code]
if lang then
cats:insert(lang:getCanonicalName() .. " modules|" .. refname)
cats:insert(("Reference modules|%s"):format(lang:getCanonicalName()))
return "This module implements the reference template {{temp|R:" .. lang_code .. ":" .. refname .. "}}."
end
end,
},
{
regex = "^Module:Quotations/([a-z-]+)/?(.*)",
process = "Quotation",
},
{
regex = "^Module:affix/lang%-data/([a-z-]+)",
process = "affix lang-data",
},
{
regex = "^Module:dialect synonyms/([a-z-]+)$",
process = function(title, cats, lang_code)
local lang = lang_cache[lang_code]
if lang then
local langname = lang:getCanonicalName()
cats:insert("Dialect synonyms data modules|" .. langname)
cats:insert(langname .. " dialect synonyms data modules| ")
return "This module contains data on specific varieties of " .. langname .. ", for use by " ..
"{{tl|dialect synonyms}}. The actual synonyms themselves are contained in submodules."
end
end,
},
{
regex = "^Module:dialect synonyms/([a-z-]+)/(.+)$",
process = function(title, cats, lang_code, term)
local lang = lang_cache[lang_code]
if lang then
local langname = lang:getCanonicalName()
cats:insert("Dialect synonyms data modules|" .. langname)
cats:insert(langname .. " dialect synonyms data modules|" .. term)
return ("This module contains dialectal %s synonyms for {{m|%s|%s}}."):format(langname, lang_code, term)
end
end,
},
}
function export.show(frame)
local boolean_default_false = {type = "boolean", default = false}
local args = require("Module:parameters").process(frame.args, {
["hr"] = true,
["for"] = true,
["from"] = true,
["allowondoc"] = boolean_default_false, -- Don't throw an error if used on a documentation subpage.
["notsubpage"] = boolean_default_false,
["nodoc"] = boolean_default_false,
["nolinks"] = boolean_default_false, -- suppress all "Useful links"
["nosandbox"] = boolean_default_false, -- supress sandbox
})
local output = Array('\n<div class="documentation" style="display:block; clear:both">\n')
local cats = Array()
local nodoc = args.nodoc
if (not args.hr) or (args.hr == "above") then
output:insert("----\n")
end
local title = args["for"] and mw.title.new(args["for"]) or mw.title.getCurrentTitle()
local doc_title = args.from ~= "-" and mw.title.new(args.from or title.fullText .. '/dokumentasi') or nil
local contentModel = title.contentModel
local pagetype, is_script_or_stylesheet = get_pagetype(title)
local preload, fallback_docs, doc_content, old_doc_title, user_name, skin_name, needs_doc
local doc_content_source = "Module:documentation"
local auto_generated_cat_source
local cats_auto_generated = false
if not args.allowondoc and is_documentation(title) then
-- TODO: merge with {{documentation subpage}}, and choose behaviour based on the page type.
error("This template should not be used on a documentation page. Please use [[Template:documentation subpage]].")
elseif is_sandbox(title) then
local sandbox_ns = title.nsText
preload = ("Template:documentation/preload%s%sSandbox"):format(
sandbox_ns == "Module" and sandbox_ns or "Template",
title.rootText:match("^[Uu]ser:(.+)") and "User" or ""
)
elseif pagetype:match("%f[%w]gadget%f[%W]") then
preload = "Template:documentation/preloadGadget"
elseif pagetype:match("%f[%w]script%f[%W]") then -- .js
if title.nsText == "MediaWiki" then
preload = "Template:documentation/preloadMediaWikiJavaScript"
else
preload = "Template:documentation/preloadTemplate" -- XXX
if title.nsText == "User" then
user_name = title.rootText
end
end
is_script_or_stylesheet = true
elseif pagetype:match("%f[%w]stylesheet%f[%W]") then -- .css
preload = "Template:documentation/preloadTemplate" -- XXX
if title.nsText == "User" then
user_name = title.rootText
end
is_script_or_stylesheet = true
elseif contentModel == "Scribunto" then -- Exclude pages in Module: which aren't Scribunto.
preload = "Template:documentation/preloadModule"
elseif pagetype:match("%f[%w]template%f[%W]") or pagetype:match("%f[%w]project%f[%W]") then
preload = "Template:documentation/preloadTemplate"
end
if doc_title and doc_title.isRedirect then
old_doc_title = doc_title
doc_title = doc_title.redirectTarget
end
output:insert("<dl class=\"plainlinks\" style=\"font-size: smaller;\">")
local function get_module_doc_and_cats(categories_only)
cats_auto_generated = true
local automatic_cats = nil
if user_name then
fallback_docs = "documentation/fallback/user module"
automatic_cats = {"User sandbox modules"}
else
for _, data in ipairs(module_regex) do
local captures = {mw.ustring.match(title.fullText, data.regex)}
if #captures > 0 then
local cat
local process_function
if type(data.process) == "function" then
process_function = data.process
elseif type(data.process) == "string" then
doc_content_source = "Module:documentation/functions/" .. data.process
process_function = require(doc_content_source)
end
if process_function then
doc_content = process_function(title, cats, unpack(captures))
end
if type(doc_content) == "table" then
doc_content_source = doc_content.title and "Template:" .. doc_content.title or doc_content_source
doc_content = mw.getCurrentFrame():expandTemplate(doc_content)
elseif doc_content and doc_content:find("{{") then
doc_content = mw.getCurrentFrame():preprocess(doc_content)
end
cat = data.cat
if cat then
if type(cat) == "string" then
cat = {cat}
end
for _, c in ipairs(cat) do
-- gsub() and Lua :gsub() return two arguments, which causes all sorts of problems.
-- Terrible design, there should have been a separate two-argument function.
local gsub_sucks = mw.ustring.gsub(title.fullText, data.regex, c)
table.insert(cats, gsub_sucks)
end
end
break
end
end
end
if title.subpageText == "templates" then
cats:insert("Template interface modules")
end
if automatic_cats then
for _, c in ipairs(automatic_cats) do
cats:insert(c)
end
end
if #cats == 0 then
local auto_cats = require("Module:module categorization").categorize(frame, "return raw", "noerror")
if #auto_cats > 0 then
auto_generated_cat_source = "Module:module categorization"
end
for _, category in ipairs(auto_cats) do
cats:insert(category)
end
end
-- meaning module is not in user’s sandbox or one of many datamodule boring series
needs_doc = not categories_only and not (automatic_cats or doc_content or fallback_docs)
end
-- Override automatic documentation, if present.
if doc_title and doc_title.exists then
local cats_auto_generated_text = ""
if contentModel == "Scribunto" then
local doc_page_content = doc_title:getContent()
-- Track then do nothing if there are uses of includeonly. The
-- pattern is slightly too permissive, but any false-positives are
-- obvious typos that should be corrected.
if doc_page_content:lower():match("</?includeonly%f[%s/>][^>]*>") then
track("module-includeonly")
else
-- Check for uses of {{module cat}}. find_templates treats the
-- input as transcluded by default (i.e. it parses the wikitext
-- which will be transcluded through to the module page).
local module_cat
for template in require("Module:template parser").find_templates(doc_page_content) do
if template:get_name() == "module cat" then
module_cat = true
break
end
end
if not module_cat then
get_module_doc_and_cats("categories only")
auto_generated_cat_source = auto_generated_cat_source or doc_content_source
cats_auto_generated_text = " Categories were auto-generated by [[" .. auto_generated_cat_source .. "]]. <sup>[[" ..
mw.title.new(auto_generated_cat_source):fullUrl{action = "edit"} .. " edit]]</sup>"
end
end
end
output:insert(
"<dd><i style=\"font-size: larger;\">Dokumentasi " ..
"ko buliah dicaliak di [[" ..
doc_title.fullText .. "]]. " .. "<sup>[[" .. doc_title:fullUrl{action = "edit"} .. " suntiang]]</sup>" ..
cats_auto_generated_text .. "</i></dd>")
else
if contentModel == "Scribunto" then
get_module_doc_and_cats(false)
elseif title.nsText == "Template" then
--cats:insert("Uncategorized templates")
needs_doc = not (fallback_docs or nodoc)
elseif user_name and is_script_or_stylesheet then
skin_name = skins[title.text:sub(#title.rootText + 1):match("^/([a-z]+)%.[jc]ss?$")]
if skin_name then
fallback_docs = "documentation/fallback/user " .. contentModel
end
end
if doc_content then
output:insert(
"<dd><i style=\"font-size: larger;\">The following " ..
"[[Help:Documenting templates and modules|documentation]] is " ..
"generated by [[" .. doc_content_source .. "]]. <sup>[[" ..
mw.title.new(doc_content_source):fullUrl{action = "edit"} ..
" edit]]</sup> </i></dd>")
elseif not nodoc then
if doc_title then
output:insert(
"<dd><i style=\"font-size: larger;\">Laman " .. pagetype ..
" ko alun ado dokumentasinyo. " ..
(fallback_docs and "Buliah " or "Eloklah ") ..
"[" .. doc_title:fullUrl{action = "edit", preload = preload}
.. " sanak nan mambueknyo].</i></dd>\n")
else
output:insert(
"<dd><i style=\"font-size: larger; color: #FF0000;\">Unable to auto-generate " ..
"documentation for this " .. pagetype ..".</i></dd>\n")
end
end
end
if title.fullText:match("^MediaWiki:Gadget%-") then
local is_gadget = false
local gadget_list = mw.title.new("MediaWiki:Gadgets-definition"):getContent()
for line in mw.text.gsplit(gadget_list, "\n") do
local gadget, opts, items = line:match("^%*%s*([A-Za-z][A-Za-z0-9_%-]*)%[(.-)%]|(.+)$") -- opts is unused
if not gadget then
gadget, items = line:match("^%*%s*([A-Za-z][A-Za-z0-9_%-]*)|(.+)$")
end
if gadget then
items = Array(mw.text.split(items, "|"))
for i, item in ipairs(items) do
if title.fullText == ("MediaWiki:Gadget-" .. item) then
is_gadget = true
output:insert("<dd> ''This script is a part of the <code>")
output:insert(gadget)
output:insert("</code> gadget ([")
output:insert(tostring(mw.uri.fullUrl("MediaWiki:Gadgets-definition", {action = "edit"})))
output:insert(" edit definitions])'' <dl>")
output:insert("<dd> ''Description ([")
output:insert(tostring(mw.uri.fullUrl("MediaWiki:Gadget-" .. gadget, {action = "edit"})))
output:insert(" edit])'': ")
local gadget_description = mw.message.new('Gadget-' .. gadget):plain()
gadget_description = frame:preprocess(gadget_description)
output:insert(gadget_description)
output:insert(" </dd>")
items:remove(i)
if #items > 0 then
for j, item in ipairs(items) do
items[j] = '[[MediaWiki:Gadget-' .. item .. '|' .. item .. ']]'
end
output:insert("<dd> ''Other parts'': ")
output:insert(mw.text.listToText(items))
output:insert("</dd>")
end
output:insert("</dl></dd>")
break
end
end
end
end
if not is_gadget then
output:insert("<dd> ''This script is not a part of any [")
output:insert(tostring(mw.uri.fullUrl("Special:Gadgets", {uselang = "en"})))
output:insert(' gadget] ([')
output:insert(tostring(mw.uri.fullUrl("MediaWiki:Gadgets-definition", {action = "edit"})))
output:insert(' edit definitions]).</dd>')
-- else
-- cats:insert("Wiktionary gadgets")
end
end
if old_doc_title then
output:insert("<dd> ''Redirected from'' [")
output:insert(old_doc_title:fullUrl{redirect = "no"})
output:insert(" ")
output:insert(old_doc_title.fullText)
output:insert("] ([")
output:insert(old_doc_title:fullUrl{action = "edit"})
output:insert(" edit]).</dd>\n")
end
if not args.nolinks then
local links = Array()
if title.isSubpage and not args.notsubpage then
links:insert("[[:" .. title.nsText .. ":" .. title.rootText .. "|root page]]")
links:insert("[[Special:PrefixIndex/" .. title.nsText .. ":" .. title.rootText .. "/|root page’s subpages]]")
else
links:insert("[[Special:PrefixIndex/" .. title.fullText .. "/|subpage list]]")
end
links:insert(
"[" .. tostring(mw.uri.fullUrl("Special:WhatLinksHere/" .. title.fullText, {hidetrans = true, hideredirs = true})) .. " links]")
if contentModel ~= "Scribunto" then
links:insert(
"[" .. tostring(mw.uri.fullUrl("Special:WhatLinksHere/" .. title.fullText, {hidelinks = true, hidetrans = true})) .. " redirects]")
end
if is_script_or_stylesheet then
if user_name then
links:insert("[[Special:MyPage" .. title.text:sub(#title.rootText + 1) .. "|your own]]")
end
else
links:insert(
"[" .. tostring(mw.uri.fullUrl("Special:WhatLinksHere/" .. title.fullText, {hidelinks = true, hideredirs = true})) .. " transclusions]")
end
if contentModel == "Scribunto" then
local is_testcases = title.isSubpage and title.subpageText == "testcases"
local without_subpage = title.nsText .. ":" .. title.baseText
if is_testcases then
links:insert("[[:" .. without_subpage .. "|tested module]]")
else
links:insert("[[" .. title.fullText .. "/testcases|testcases]]")
end
if user_name then
links:insert("[[User:" .. user_name .. "|user page]]")
links:insert("[[User talk:" .. user_name .. "|user talk page]]")
links:insert("[[Special:PrefixIndex/User:" .. user_name .. "/|userspace]]")
else
-- If sandbox module, add a link to the module that this is a sandbox of.
-- Exclude user sandbox modules like [[User:Dine2016/sandbox]].
if title.text:find("/sandbox%d*%f[/%z]") then
cats:insert("Sandbox modules")
track("sandbox to be moved")
-- Sandbox modules don’t really need documentation.
needs_doc = false
-- Will behave badly if “/sandbox” occurs twice in title!
local sandbox_of = title.fullText:gsub("/sandbox%d*%f[/%z]", "")
local diff
if page_exists(sandbox_of) then
diff = " (" .. compare_pages(title.fullText, sandbox_of, "diff") .. ")"
else
track("no sandbox of")
end
links:insert("[[:" .. sandbox_of .. "|sandbox of]]" .. (diff or ""))
-- If not a sandbox module, add link to sandbox module.
-- Sometimes there are multiple sandboxes for a single module:
-- [[Module:sa-pronunc/sandbox]], [[Module:sa-pronunc/sandbox2]].
-- Occasionally sandbox modules have their own subpages that are also
-- sandboxes: [[Module:grc-decl/sandbox/decl]].
else
local sandbox_title
if title.fullText:find("^Module:grc%-decl/") then
sandbox_title = title.fullText:gsub("^Module:grc%-decl/", "Module:grc-decl/sandbox/")
elseif is_testcases then
sandbox_title = title.fullText:gsub("/testcases", "/sandbox/testcases")
else
sandbox_title = title.fullText .. "/sandbox"
end
local sandbox_link = "[[:" .. sandbox_title .. "|sandbox]]"
local diff
if page_exists(sandbox_title) then
diff = " (" .. compare_pages(title.fullText, sandbox_title, "diff") .. ")"
end
links:insert(sandbox_link .. (diff or ""))
end
end
end
if title.nsText == "Template" then
-- Error search: all(any namespace), hastemplate (show pages using the template), insource (show source code), incategory (any/specific error) -- [[mw:Help:CirrusSearch]], [[w:Help:Searching/Regex]]
-- apparently same with/without: &profile=advanced&fulltext=1
local errorq = 'searchengineselect=mediawiki&search=all: hastemplate:\"'..title.rootText..'\" insource:\"'..title.rootText..'\" incategory:'
local eincategory = "Pages_with_module_errors|ParserFunction_errors|DisplayTitle_errors|Pages_with_ISBN_errors|Pages_with_ISSN_errors|Pages_with_reference_errors|Pages_with_syntax_highlighting_errors|Pages_with_TemplateStyles_errors"
links:insert(
'[' .. tostring(mw.uri.fullUrl('Special:Search', errorq..eincategory )) .. ' errors]'
.. ' (' ..
'[' .. tostring(mw.uri.fullUrl('Special:Search', errorq..'ParserFunction_errors' )) .. ' parser]'
.. '/' ..
'[' .. tostring(mw.uri.fullUrl('Special:Search', errorq..'Pages_with_module_errors' )) .. ' module]'
.. ')'
)
if title.isSubpage and title.text:find("/sandbox%d*%f[/%z]") then -- This is a sandbox template.
-- At the moment there are no user sandbox templates with subpage
-- “/sandbox”.
cats:insert("Sandbox templates")
-- Sandbox templates don’t really need documentation.
needs_doc = false
-- Will behave badly if “/sandbox” occurs twice in title!
local sandbox_of = title.fullText:gsub("/sandbox%d*%f[/%z]", "")
local diff
if page_exists(sandbox_of) then
diff = " (" .. compare_pages(title.fullText, sandbox_of, "diff") .. ")"
else
track("no sandbox of")
end
links:insert("[[:" .. sandbox_of .. "|sandbox of]]" .. (diff or ""))
-- This is a template that can have a sandbox.
elseif not args.nosandbox then -- unless we tell it not to
local sandbox_title = title.fullText .. "/sandbox"
local diff
if page_exists(sandbox_title) then
diff = " (" .. compare_pages(title.fullText, sandbox_title, "diff") .. ")"
end
links:insert("[[:" .. sandbox_title .. "|sandbox]]" .. (diff or ""))
end
end
if #links > 0 then
output:insert("<dd> ''Useful links'': " .. links:concat(" • ") .. "</dd>")
end
end
output:insert("</dl>\n")
-- Show error from [[Module:category tree/topic cat/data]] on its submodules'
-- documentation to, for instance, warn about duplicate labels.
if title.fullText:find("Module:category tree/topic cat/data", 1, true) == 1 then
local ok, err = pcall(require, "Module:category tree/topic cat/data")
if not ok then
output:insert('<span class="error">' .. err .. '</span>\n\n')
end
end
if doc_title and doc_title.exists then
-- Override automatic documentation, if present.
doc_content = frame:expandTemplate { title = doc_title.fullText }
elseif not doc_content and fallback_docs then
doc_content = frame:expandTemplate {
title = fallback_docs,
args = {
['user'] = user_name,
['page'] = title.fullText,
['skin name'] = skin_name,
},
}
end
if doc_content then
output:insert(doc_content)
end
output:insert(('\n<%s style="clear: both;" />'):format(args.hr == "below" and "hr" or "br"))
if cats_auto_generated and not cats[1] and (not doc_content or not doc_content:find("%[%[Category:")) then
if contentModel == "Scribunto" then
cats:insert("Uncategorized modules")
-- elseif title.nsText == "Template" then
-- cats:insert("Uncategorized templates")
end
end
if needs_doc then
cats:insert("Templates and modules needing documentation")
end
for _, cat in ipairs(cats) do
output:insert("[[Category:" .. cat .. "]]")
end
output:insert("</div>\n")
return output:concat()
end
function export.module_auto_doc_table()
local parts = {}
local function ins(text)
table.insert(parts, text)
end
ins('{|class="wikitable"')
ins("! Regex !! Category !! Handling modules")
for _, spec in ipairs(module_regex) do
local cat_text
local cats = spec.cat
if cats then
local cat_parts = {}
if type(cats) == "string" then
cats = {cats}
end
for _, cat in ipairs(cats) do
table.insert(cat_parts, ("<code>%s</code>"):format((cat:gsub("|", "|"))))
end
cat_text = table.concat(cat_parts, ", ")
else
cat_text = "''(unspecified)''"
end
ins("|-")
ins(("| <code>%s</code> || %s || %s"):format(spec.regex, cat_text,
type(spec.process) == "function" and "''(handled internally)''" or
type(spec.process) == "string" and ("[[Module:documentation/functions/%s]]"):format(spec.process) or
"''(no documentation generator)''"))
end
ins("|}")
return table.concat(parts, "\n")
end
-- Used by {{translit module documentation}}.
function export.translitModuleLangList(frame)
local pagename, subpage
if frame.args[1] then
pagename = frame.args[1]
else
local title = mw.title.getCurrentTitle()
subpage = title.subpageText
pagename = title.text
if subpage ~= pagename then
pagename = title.rootText
end
end
local translitModule = pagename
local languageObjects = require("Module:languages/byTranslitModule")(translitModule)
local codeInPagename = pagename:match("^([%l-]+)%-.*translit$")
local categories = Array()
local codeInPagenameInList = false
if codeInPagename then
if languageObjects[1] and subpage ~= "documentation" then
local agreement = languageObjects[2] and "s" or ""
categories:insert("[[Category:Transliteration modules used by " ..
#languageObjects .. " language" .. agreement .. "]]")
end
languageObjects = Array(languageObjects)
:filter(
function (lang)
local result = lang:getCode() ~= codeInPagename
codeInPagenameInList = codeInPagenameInList or result
return result
end)
end
if subpage ~= "documentation" then
for script_code in pagename:gmatch("%f[^-%z]%u%l%l%l%f[-]") do
local script = require "Module:scripts".getByCode(script_code)
if script then
categories:insert("[[Category:" .. script:getCategoryName() .. "]]")
end
end
end
if subpage ~= "documentation" and not page_exists("Module:" .. pagename .. "/testcases") then
categories:insert("[[Category:Transliteration modules without a testcases subpage]]")
end
if not languageObjects[1] then
return categories:concat()
end
local langs = Array(languageObjects)
:sort(
function(lang1, lang2)
return lang1:getCode() < lang2:getCode()
end)
-- This will not error because languageObjects is not empty.
:map(languageObjects[1].makeCategoryLink)
:serialCommaJoin()
return "It is " .. ( codeInPagenameInList and "also" or "" ) ..
" used to transliterate " .. langs .. "." .. categories:concat()
end
-- Used by {{entry name module documentation}}.
function export.entryNameModuleLangList(frame)
local pagename, subpage
if frame.args[1] then
pagename = frame.args[1]
else
local title = mw.title.getCurrentTitle()
subpage = title.subpageText
pagename = title.text
if subpage ~= pagename then
pagename = title.rootText
end
end
local entryNameModule = pagename
local languageObjects = require("Module:languages/byEntryNameModule")(entryNameModule)
local codeInPagename = pagename:match("^([%l-]+)%-.*entryname$")
local categories = Array()
local codeInPagenameInList = false
if codeInPagename then
if languageObjects[1] and subpage ~= "documentation" then
local agreement = languageObjects[2] and "s" or ""
categories:insert("[[Category:Entry name-generating modules used by " ..
#languageObjects .. " language" .. agreement .. "]]")
end
languageObjects = Array(languageObjects)
:filter(
function (lang)
local result = lang:getCode() ~= codeInPagename
codeInPagenameInList = codeInPagenameInList or result
return result
end)
end
if subpage ~= "documentation" then
for script_code in pagename:gmatch("%f[^-%z]%u%l%l%l%f[-]") do
local script = require "Module:scripts".getByCode(script_code)
if script then
categories:insert("[[Category:" .. script:getCategoryName() .. "]]")
end
end
end
if subpage ~= "documentation" and not page_exists("Module:" .. pagename .. "/testcases") then
categories:insert("[[Category:Entry name-generating modules without a testcases subpage]]")
end
if not languageObjects[1] then
return categories:concat()
end
local langs = Array(languageObjects)
:sort(
function(lang1, lang2)
return lang1:getCode() < lang2:getCode()
end)
-- This will not error because languageObjects is not empty.
:map(languageObjects[1].makeCategoryLink)
:serialCommaJoin()
return "It is " .. ( codeInPagenameInList and "also" or "" ) ..
" used to generate entry names for " .. langs .. "." .. categories:concat()
end
-- Used by {{sortkey module documentation}}.
function export.sortkeyModuleLangList(frame)
local pagename, subpage
if frame.args[1] then
pagename = frame.args[1]
else
local title = mw.title.getCurrentTitle()
subpage = title.subpageText
pagename = title.text
if subpage ~= pagename then
pagename = title.rootText
end
end
local sortkeyModule = pagename
local languageObjects = require("Module:languages/bySortkeyModule")(sortkeyModule)
local codeInPagename = pagename:match("^([%l-]+)%-.*sortkey$")
local categories = Array()
local codeInPagenameInList = false
if codeInPagename then
if languageObjects[1] and subpage ~= "documentation" then
local agreement = languageObjects[2] and "s" or ""
categories:insert("[[Category:Sortkey-generating modules used by " ..
#languageObjects .. " language" .. agreement .. "]]")
end
languageObjects = Array(languageObjects)
:filter(
function (lang)
local result = lang:getCode() ~= codeInPagename
codeInPagenameInList = codeInPagenameInList or result
return result
end)
end
if subpage ~= "documentation" then
for script_code in pagename:gmatch("%f[^-%z]%u%l%l%l%f[-]") do
local script = require "Module:scripts".getByCode(script_code)
if script then
categories:insert("[[Category:" .. script:getCategoryName() .. "]]")
end
end
end
if subpage ~= "documentation" and not page_exists("Module:" .. pagename .. "/testcases") then
categories:insert("[[Category:Sortkey-generating modules without a testcases subpage]]")
end
if not languageObjects[1] then
return categories:concat()
end
local langs = Array(languageObjects)
:sort(
function(lang1, lang2)
return lang1:getCode() < lang2:getCode()
end)
-- This will not error because languageObjects is not empty.
:map(languageObjects[1].makeCategoryLink)
:serialCommaJoin()
return "It is " .. ( codeInPagenameInList and "also" or "" ) ..
" used to sort " .. langs .. "." .. categories:concat()
end
return export
msokkj6kop683439xbz2s1acifxma8j
Modul:Pages
828
2548
10382
2026-09-16T08:22:29Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local string_utilities_module = "Module:string utilities" local concat = table.concat local find = string.find local format = string.format local getmetatable = getmetatable local get_current_section -- Defined below. local get_pagetype -- Defined below. local gsub = string.gsub local insert = table.insert local is_internal_title -- Defined below. local is_title -- Defined below. local lower = string.lower local match = string.match local new_tit..."
10382
Scribunto
text/plain
local export = {}
local string_utilities_module = "Module:string utilities"
local concat = table.concat
local find = string.find
local format = string.format
local getmetatable = getmetatable
local get_current_section -- Defined below.
local get_pagetype -- Defined below.
local gsub = string.gsub
local insert = table.insert
local is_internal_title -- Defined below.
local is_title -- Defined below.
local lower = string.lower
local match = string.match
local new_title = mw.title.new
local require = require
local sub = string.sub
local title_equals = mw.title.equals
local tonumber = tonumber
local type = type
local ufind = mw.ustring.find
local unstrip_nowiki = mw.text.unstripNoWiki
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function ulower(...)
ulower = require(string_utilities_module).lower
return ulower(...)
end
local function trim(...)
trim = require(string_utilities_module).trim
return trim(...)
end
--[==[
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==]
local current_frame
local function get_current_frame()
current_frame, get_current_frame = mw.getCurrentFrame(), nil
return current_frame
end
local parent_frame
local function get_parent_frame()
parent_frame, get_parent_frame = (current_frame or get_current_frame()):getParent(), nil
return parent_frame
end
local namespace_shortcuts
local function get_namespace_shortcuts()
namespace_shortcuts, get_namespace_shortcuts = {
[4] = "WT",
[10] = "T",
[14] = "CAT",
[100] = "AP",
[110] = "WS",
[118] = "RC",
[828] = "MOD",
}, nil
return namespace_shortcuts
end
do
local transcluded
--[==[
Returns {true} if the current {{tl|#invoke:}} is being transcluded, or {false} if not. If the current {{tl|#invoke:}} is part of a template, for instance, this template will therefore return {true}.
Note that if a template containing an {{tl|#invoke:}} is used on its own page (e.g. to display a demonstration), this function is still able to detect that this is transclusion. This is an improvement over the other method for detecting transclusion, which is to check the parent frame title against the current page title, which fails to detect transclusion in that instance.]==]
function export.is_transcluded()
if transcluded == nil then
transcluded = (parent_frame or get_parent_frame()) and parent_frame:preprocess("<includeonly>1</includeonly>") == "1" or false
end
return transcluded
end
end
do
local preview
--[==[
Returns {true} if the page is currently being viewed in preview, or {false} if not.]==]
function export.is_preview()
if preview == nil then
preview = (current_frame or get_current_frame()):preprocess("{{REVISIONID}}") == ""
end
return preview
end
end
--[==[
Returns {true} if the input is a title object, or {false} if not. This therefore '''includes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_internal_title` below.]==]
function export.is_title(val)
if not (val and type(val) == "table") then
return false
end
local mt = getmetatable(val)
-- There's no foolproof method for checking for a title object, but the
-- __eq metamethod should be mw.title.equals unless the object has been
-- seriously messed around with.
return mt and
type(mt) == "table" and
getmetatable(mt) == nil and
mt.__eq == title_equals and
true or false
end
is_title = export.is_title
--[==[
Returns {true} if the input is an internal title object, or {false} if not. An internal title object is a title object for a page on this wiki, such as [[example]]. This therefore '''excludes''' external title objects (i.e. those for pages on other wikis), such as [[w:Example]], unlike `is_title` above.]==]
function export.is_internal_title(title)
-- Note: Mainspace titles starting with "#" should be invalid, but a bug in mw.title.new and mw.title.makeTitle means a title object is returned that has the empty string for prefixedText, so they need to be filtered out.
return is_title(title) and #title.prefixedText > 0 and #title.interwiki == 0
end
is_internal_title = export.is_internal_title
--[==[
Returns {true} if the input string is a valid link target, or {false} if not. This therefore '''includes''' link targets to other wikis, such as [[w:Example]], unlike `is_valid_page_name` below.]==]
function export.is_valid_link_target(target)
local target_type = type(target)
if target_type == "string" then
return is_title(new_title(target))
end
error(format("bad argument #1 to 'is_valid_link_target' (string expected, got %s)", target_type), 2)
end
--[==[
Returns {true} if the input string is a valid page name on this wiki, or {false} if not. This therefore '''excludes''' page names on other wikis, such as [[w:Example]], unlike `is_valid_link_target` above.]==]
function export.is_valid_page_name(name)
local name_type = type(name)
if name_type == "string" then
return is_internal_title(new_title(name))
end
error(format("bad argument #1 to 'is_valid_page_name' (string expected, got %s)", name_type), 2)
end
do
local function find_sandbox(text)
return find(text, "^User:.") or find(lower(text), "sandbox", 1, true)
end
local function get_transclusion_subtypes(title, main_type, documentation, page_suffix)
local text, subtypes = title.text, {main_type}
-- Any template/module with "sandbox" in the title. These are impossible
-- to screen for more accurately, as there's no consistent pattern. Also
-- any user sandboxes in the form (e.g.) "Template:User:...".
local sandbox = find_sandbox(text)
if sandbox then
insert(subtypes, "sandbox")
end
-- Any template/module testcases (which can be labelled and/or followed
-- by further subpages).
local testcase = find(text, "./[Tt]estcases?%f[%L]")
if testcase then
-- Order "testcase" and "sandbox" based on where the patterns occur
-- in the title.
local n = sandbox and sandbox < testcase and 3 or 2
insert(subtypes, n, "testcase")
end
-- Any template/module documentation pages.
if documentation then
insert(subtypes, "documentation")
end
local final = subtypes[#subtypes]
if not (final == main_type and not page_suffix or final == "sandbox") then
insert(subtypes, "page")
end
return concat(subtypes, " ")
end
local function get_snippet_subtypes(title, main_type, documentation)
local ns = title.namespace
return get_transclusion_subtypes(title, (
ns == 2 and "user " or
ns == 8 and match(title.text, "^Gadget-.") and "gadget " or
""
) .. main_type, documentation)
end
--[==[
Returns the page type of the input title object in a format which can be used in running text.]==]
function export.get_pagetype(title)
if not is_internal_title(title) then
error(mw.dumpObject(title.fullText) .. " is not a valid page name.")
end
-- If possibly a documentation page, get the parent and set the
-- `documentation` flag.
local content_model, text, documentation = title.contentModel
if content_model == "wikitext" then
text = title.text
if match(text, "./documentation$") then
local parent_title = new_title(sub(title.fullText, 1, -15))
if parent_title then
title = parent_title
content_model, text, documentation = title.contentModel, title.text, true
end
end
end
-- Content models have overriding priority, as they can appear in
-- nonstandard places due to page content model changes.
if content_model == "css" or content_model == "sanitized-css" then
return get_snippet_subtypes(title, "stylesheet", documentation)
elseif content_model == "javascript" then
return get_snippet_subtypes(title, "script", documentation)
elseif content_model == "json" then
return get_snippet_subtypes(title, "JSON data", documentation)
elseif content_model == "MassMessageListContent" then
return get_snippet_subtypes(title, "mass message delivery list", documentation)
-- Modules.
elseif content_model == "Scribunto" then
return get_transclusion_subtypes(title, "module", documentation, false)
elseif content_model == "text" then
return "page" -- ???
-- Otherwise, the content model is "wikitext", so check namespaces.
elseif title.isTalkPage then
return "talk page"
end
local ns = title.namespace
-- Main namespace.
if ns == 0 then
return "entry"
-- Wiktionary:
elseif ns == 4 then
return find_sandbox(title.text) and "sandbox" or "project page"
-- Template:
elseif ns == 10 then
return get_transclusion_subtypes(title, "templat", documentation, false)
end
-- Convert the namespace to lowercase, unless it contains a capital
-- letter after the initial letter (e.g. MediaWiki, TimedText). Also
-- normalize any underscores.
local ns_text = gsub(title.nsText, "_", " ")
if ufind(ns_text, "^%U*$", 2) then
ns_text = ulower(ns_text)
end
-- User:
if ns == 2 then
return ns_text .. " " .. (title.isSubpage and "subpage" or "page")
-- Category: and Appendix:
elseif ns == 14 or ns == 100 then
return ns_text
-- Thesaurus: and Reconstruction:
elseif ns == 110 or ns == 118 then
return ns_text .. " entry"
end
return ns_text .. " page"
end
get_pagetype = export.get_pagetype
end
--[==[
Returns {true} if the input title object is for a content page, or {false} if not. A content page is a page that is considered part of the dictionary itself, and excludes pages for discussion, administration, maintenance etc.]==]
function export.is_content_page(title)
if not is_internal_title(title) then
error(mw.dumpObject(title.fullText) .. " is not a valid page name.")
end
local ns = title.namespace
-- (main), Appendix, Thesaurus, Citations, Reconstruction.
return (ns == 0 or ns == 100 or ns == 110 or ns == 114 or ns == 118) and
title.contentModel == "wikitext"
end
--[==[
Returns {true} if the input title object is for a documentation page, or {false} if not.]==]
function export.is_documentation(title)
return match(get_pagetype(title), "%f[%w]documentation%f[%W]") and true or false
end
--[==[
Returns {true} if the input title object is for a sandbox, or {false} if not.
By default, sandbox documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==]
function export.is_sandbox(title, include_documentation)
local pagetype = get_pagetype(title)
return match(pagetype, "%f[%w]sandbox%f[%W]") and (
include_documentation or
not match(pagetype, "%f[%w]documentation%f[%W]")
) and true or false
end
--[==[
Returns {true} if the input title object is for a testcase page, or {false} if not.
By default, testcase documentation pages are excluded, but this can be overridden with the `include_documentation` parameter.]==]
function export.is_testcase_page(title, include_documentation)
local pagetype = get_pagetype(title)
return match(pagetype, "%f[%w]testcase%f[%W]") and (
include_documentation or
not match(pagetype, "%f[%w]documentation%f[%W]")
) and true or false
end
--[==[
Returns the namespace shortcut for the input title object, or else the namespace text. For example, a `Template:` title returns {"T"}, a `Module:` title returns {"MOD"}, and a `User:` title returns {"User"}.]==]
function export.get_namespace_shortcut(title)
return (namespace_shortcuts or get_namespace_shortcuts())[title.namespace] or title.nsText
end
do
local function check_level(lvl)
if type(lvl) ~= "number" then
error("Heading levels must be numbers.")
elseif lvl < 1 or lvl > 6 or lvl % 1 ~= 0 then
error("Heading levels must be integers between 1 and 6.")
end
return lvl
end
--[==[
A helper function which iterates over the headings in `text`, which should be the content of a page or (main) section.
Each iteration returns three values: `sec` (the section title), `lvl` (the section level) and `loc` (the index of the section in the given text, from the first equals sign). The section title will be automatically trimmed, and any HTML entities will be resolved.
The optional parameter `a` (which should be an integer between 1 and 6) can be used to ensure that only headings of the specified level are iterated over. If `b` is also given, then they are treated as a range.
The optional parameters `a` and `b` can be used to specify a range, so that only headings with levels in that range are returned.]==]
local function find_headings(text, a, b)
a = a and check_level(a) or nil
b = b and check_level(b) or a or nil
local start, loc, lvl, sec = 1
return function()
repeat
loc, lvl, sec, start = match(text, "()%f[^%z\n](==?=?=?=?=?)([^\n]+)%2[\t ]*%f[%z\n]()", start)
lvl = lvl and #lvl
until not (sec and a) or (lvl >= a and lvl <= b)
return sec and trim(decode_entities(sec)) or nil, lvl, loc
end
end
local function _get_section(content, name, level)
if not (content and name) then
return nil
elseif find(name, "\n", 1, true) then
error("Heading name cannot contain a newline.")
end
level = level and check_level(level) or nil
name = trim(decode_entities(name))
local start
for sec, lvl, loc in find_headings(content, level and 1 or nil, level) do
if start and lvl <= level then
return sub(content, start, loc - 1)
elseif not start and (not level or lvl == level) and sec == name then
start, level = loc, lvl
end
end
return start and sub(content, start)
end
--[==[
A helper function to return the content of a page section.
`content` is raw wikitext, `name` is the requested section, and `level` is an optional parameter that specifies
the required section heading level. If `level` is not supplied, then the first section called `name` is returned.
`name` can either be a string or table of section names. If a table, each name represents a section that has the
next as a subsection. For example, { {"Spanish", "Noun"}} will return the first matching section called "Noun"
under a section called "Spanish". These do not have to be at adjacent levels ("Noun" might be L4, while "Spanish"
is L2). If `level` is given, it refers to the last name in the table (i.e. the name of the section to be returned).
The returned section includes all of its subsections. If no matching section is found, return {nil}.]==]
function export.get_section(content, names, level)
if type(names) ~= "table" then
return _get_section(content, names, level)
end
local i = 1
local name = names[i]
if not name then
error("Must specify at least 1 section.")
end
while true do
local nxt_i = i + 1
local nxt = names[nxt_i]
if nxt == nil then
return _get_section(content, name, level)
end
content = _get_section(content, name)
if content == nil then
return nil
elseif i == 6 then
error("Not possible specify more than 6 sections: headings only go up to level 6.")
end
i = nxt_i
name = names[i]
end
return content
end
end
do
local current_section
--[==[
A function which returns the number of the page section which contains the current {#invoke}.]==]
function export.get_current_section()
if current_section ~= nil then
return current_section
end
local extension_tag = (current_frame or get_current_frame()).extensionTag
-- We determine the section via the heading strip marker count, since they're numbered sequentially, but the only way to do this is to generate a fake heading via frame:preprocess(). The native parser assigns each heading a unique marker, but frame:preprocess() will return copies of older markers if the heading is identical to one further up the page, so the fake heading has to be unique to the page. The best way to do this is to feed it a heading containing a nowiki marker (which we will need later), since those are always unique.
local nowiki_marker = extension_tag(current_frame, "nowiki")
-- Note: heading strip markers have a different syntax to the ones used for tags.
local h = tonumber(match(
current_frame:preprocess("=" .. nowiki_marker .. "="),
"\127'\"`UNIQ%-%-h%-(%d+)%-%-QINU`\"'\127"
))
-- For some reason, [[Special:ExpandTemplates]] doesn't generate a heading strip marker, so if that happens we simply abort early.
if not h then
return 0
end
-- The only way to get the section number is to increment the heading count, so we store the offset in nowiki strip markers which can be retrieved by procedurally unstripping nowiki markers, counting backwards until we find a match.
local n, offset = tonumber(match(
nowiki_marker,
"\127'\"`UNIQ%-%-nowiki%-([%dA-F]+)%-QINU`\"'\127"
), 16)
while not offset and n > 0 do
n = n - 1
offset = match(
unstrip_nowiki(format("\127'\"`UNIQ--nowiki-%08X-QINU`\"'\127", n)),
"^HEADING\1(%d+)" -- Prefix "HEADING\1" prevents collisions.
)
end
offset = offset and (offset + 1) or 0
extension_tag(current_frame, "nowiki", "HEADING\1" .. offset)
current_section = h - offset
return current_section
end
get_current_section = export.get_current_section
end
do
local L2_sections
local function get_L2_sections()
L2_sections, get_L2_sections = mw.loadData("Module:headword/data").page.L2_sections, nil
return L2_sections
end
--[==[
A function which returns the name of the L2 language section which contains the current {#invoke}.]==]
function export.get_current_L2()
local section = get_current_section()
if section == 0 then
return
end
while section > 0 do
local L2 = (L2_sections or get_L2_sections())[section]
if L2 then
return L2
end
section = section - 1
end
end
end
return export
jll6o3u1l66k1tc2givdb9h08mcqy58
Modul:Array
828
2549
10384
2026-09-16T08:24:39Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local debug_track_module = "Module:debug/track" local function_module = "Module:fun" local table_module = "Module:table" local get_array_mt -- Defined below. local getmetatable = getmetatable local ipairs = ipairs local pairs = pairs local rawget = rawget local rawset = rawset local require = require local select = select local setmetatable = setmetatable local sort = table.sort local type = type local unpack = unpack or table.unpack -- Lua 5.2 c..."
10384
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local function_module = "Module:fun"
local table_module = "Module:table"
local get_array_mt -- Defined below.
local getmetatable = getmetatable
local ipairs = ipairs
local pairs = pairs
local rawget = rawget
local rawset = rawset
local require = require
local select = select
local setmetatable = setmetatable
local sort = table.sort
local type = type
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local upper = string.upper
local array_mt -- Defined below.
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function append(...)
append = require(table_module).append
return append(...)
end
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function deep_copy(...)
deep_copy = require(table_module).deepCopy
return deep_copy(...)
end
local function list_to_set(...)
list_to_set = require(table_module).listToSet
return list_to_set(...)
end
local function shallow_copy(...)
shallow_copy = require(table_module).shallowCopy
return shallow_copy(...)
end
--[==[
Loaders for objects, which load data (or some other object) into some variable, which can then be accessed as "foo or get_foo()", where the function get_foo sets the object to "foo" and then returns it. This ensures they are only loaded when needed, and avoids the need to check for the existence of the object each time, since once "foo" has been set, "get_foo" will not be called again.]==]
local m_function
local function get_m_function()
m_function, get_m_function = require(function_module), nil
return m_function
end
local m_table
local function get_m_table()
m_table, get_m_table = require(table_module), nil
return m_table
end
-- Functions from [[Module:table]] that operate on arrays or sparse arrays.
-- List copied from [[Module:table/documentation]].
local m_table_array_funcs
local function get_m_table_array_funcs()
m_table_array_funcs = list_to_set{
-- non-sparse
"signedIndex", "append", "extend", "extendIfNot", "slice", "removeDuplicates", "length",
"size", "contains", "serialCommaJoin", "reverseIpairs", "reverse",
"invert", "listToSet", "flatten", "isArray",
-- sparse
"numKeys", "maxIndex", "compressSparseArray", "indexPairs", "indexIpairs",
"sparseIpairs",
-- tables in general
"shallowCopy", "deepCopy"
}
get_m_table_array_funcs = nil
return m_table_array_funcs
end
-- Functions from [[Module:fun]] that take an array in the second argument.
-- They just have to have the argument order reversed to work as methods of the
-- array object.
local m_function_array_funcs
local function get_m_function_array_funcs()
m_function_array_funcs = list_to_set{
"map", "some", "all", "filter", "fold"
}
get_m_function_array_funcs = nil
return m_function_array_funcs
end
-- Functions from [[Module:table]] that create an array or table.
-- Not all of these operate on arrays.
local m_table_new_array_funcs
local function get_m_table_new_array_funcs()
m_table_new_array_funcs = list_to_set{
-- Array.
"append", "slice", "removeDuplicates", "numKeys", "compressSparseArray",
"keysToList", "reverse", "flatten",
-- Array or table.
"shallowCopy", "deepCopy"
}
get_m_table_new_array_funcs = nil
return m_table_new_array_funcs
end
-- Functions from [[Module:fun]] that create an array or table.
-- Not all of these operate on arrays.
local m_function_new_array_funcs
local function get_m_function_new_array_funcs()
m_function_new_array_funcs = list_to_set{
"map", "filter",
}
get_m_function_new_array_funcs = nil
return m_function_new_array_funcs
end
-- Add aliases for the functions from [[Module:table]] whose names
-- contain "array" or "list", which is redundant.
-- The key redirects to the value.
local alias_of = {
compress = "compressSparseArray",
keys = "keysToList",
toSet = "listToSet",
}
local function underscore_to_camel_case(str)
if type(str) ~= "string" then
return str
end
local ret = str:gsub("_(.)", upper)
if ret ~= str then
debug_track("array/underscore to camel case")
end
return ret
end
local function get_module_function(key, module, module_name)
return module[key] or
error(("Cannot find %s in [[Module:%s]]"):format(mw.dumpObject(key), module_name))
end
local function wrap_in_array_constructor(func)
return function (...)
return setmetatable(func(...), array_mt or get_array_mt())
end
end
function get_array_mt()
-- Copy table library so as not to unexpectedly change the behavior of code that
-- uses it.
local Array = deep_copy(table)
Array.ipairs = ipairs
Array.pairs = pairs
Array.unpack = unpack
Array.listToText = mw.text.listToText
-- Create version of table.sort that returns the table.
function Array:sort(comp)
sort(self, comp)
return self
end
function Array:type()
local mt = getmetatable(self)
return mt and type(mt) == "table" and rawget(mt, "__type") or nil
end
local Array_mt = {}
setmetatable(Array, Array_mt)
function Array_mt:__index(key)
if type(key) ~= "string" then
return nil
end
-- Convert underscores to camel case: num_keys -> numKeys.
-- FIXME: this is pointless overhead: remove once nothing relies on it.
key = underscore_to_camel_case(key)
key = alias_of[key] or key
local func = rawget(self, key)
if func ~= nil then
return func
elseif (m_table_array_funcs or get_m_table_array_funcs())[key] then
func = get_module_function(key, m_table or get_m_table(), "table")
if (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then
func = wrap_in_array_constructor(func)
end
elseif (m_function_array_funcs or get_m_function_array_funcs())[key] then
local raw_func = get_module_function(key, m_function or get_m_function(), "fun")
--[==[ Once isArray is no longer used:
function func(a, b, ...)
return raw_func(b, a, ...)
end
]==]
if key == "fold" then
function func(t, f, accum)
return raw_func(f, t, accum)
end
else
function func(a, b) -- TODO: isArray parameter is probably unnecessary, and doesn't work with sparse arrays anyway.
debug_track("array/isArray")
return raw_func(b, a, "isArray")
end
end
if (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then
func = wrap_in_array_constructor(func)
end
else
return nil
end
rawset(Array, key, func)
return func
end
array_mt = {
__index = Array,
__type = "array",
}
function array_mt:__add(v)
return setmetatable(append(self, v), array_mt or get_array_mt())
end
get_array_mt = nil
return array_mt
end
-- A function to convert string key-table modules such
-- as [[Module:languages/data/2]] into arrays.
-- "from" is a bad name.
-- field_for_key supplies the field name in which the
-- key will be stored.
function export.from(map, field_for_key)
local arr, i = {}, 0
for key, val in pairs(map) do
i = i + 1
local new_val = shallow_copy(val)
if field_for_key then
new_val[field_for_key] = key
end
arr[i] = new_val
end
return setmetatable(arr, array_mt or get_array_mt())
end
local export_mt = {}
function export_mt:__call(...)
local arr
if select("#", ...) == 1 and type((...)) == "table" then
arr = ...
local mt = getmetatable(arr)
-- If table has been loaded with mw.loadData, copy it to avoid the
-- limitations of it being a virtual table.
if mt and type(mt) == "table" and rawget(mt, "mw_loadData") == true then
arr = shallow_copy(arr)
end
else
arr = {...}
end
return setmetatable(arr, array_mt or get_array_mt())
end
function export_mt:__index(key)
-- Convert underscores to camel case: num_keys -> numKeys.
-- FIXME: this is pointless overhead: remove once nothing relies on it.
key = underscore_to_camel_case(key)
key = alias_of[key] or key
local func = rawget(self, key)
if func ~= nil then
return func
elseif (m_table_new_array_funcs or get_m_table_new_array_funcs())[key] then
func = get_module_function(key, m_table or get_m_table(), "table")
elseif (m_function_new_array_funcs or get_m_function_new_array_funcs())[key] then
func = get_module_function(key, m_function or get_m_function(), "fun")
else
return nil
end
func = wrap_in_array_constructor(func)
rawset(export, key, func)
return func
end
return setmetatable(export, export_mt)
sxt52fsnry0sg9endqo2zu3aaj23mkn
Modul:Parameters
828
2550
10385
2026-09-16T08:25:40Z
Zhilal Darma
23
←Mambuek laman baisi "--[==[TODO: * Change certain flag names, as some are misnomers: * Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`. * Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted. * Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that. ]==] local export = {} local collation_module = "Module:collatio..."
10385
Scribunto
text/plain
--[==[TODO:
* Change certain flag names, as some are misnomers:
* Change `allow_holes` to `keep_holes`, because it's not the inverse of `disallow_holes`.
* Change `allow_empty` to `keep_empty`, as it causes them to be kept as "" instead of deleted.
* Sort out all the internal error calls. Manual error(format()) calls are used when certain parameters shouldn't be dumped, so find a way to avoid that.
]==]
local export = {}
local collation_module = "Module:collation"
local families_module = "Module:families"
local functions_module = "Module:fun"
local gender_and_number_utilities_module = "Module:gender and number utilities"
local labels_module = "Module:labels"
local languages_module = "Module:languages"
local math_module = "Module:math"
local pages_module = "Module:pages"
local parameters_finalize_set_module = "Module:parameters/finalizeSet"
local parameters_track_module = "Module:parameters/track"
local parse_utilities_module = "Module:parse utilities"
local references_module = "Module:references"
local scribunto_module = "Module:Scribunto"
local scripts_module = "Module:scripts"
local string_utilities_module = "Module:string utilities"
local table_module = "Module:table"
local wikimedia_languages_module = "Module:wikimedia languages"
local yesno_module = "Module:yesno"
local mw = mw
local mw_title = mw.title
local string = string
local table = table
local dump = mw.dumpObject
local find = string.find
local format = string.format
local gsub = string.gsub
local insert = table.insert
local ipairs = ipairs
local list_to_text = mw.text.listToText
local make_title = mw_title.makeTitle
local match = string.match
local max = math.max
local new_title = mw_title.new
local next = next
local pairs = pairs
local pcall = pcall
local require = require
local sub = string.sub
local tonumber = tonumber
local type = type
local unpack = unpack or table.unpack -- Lua 5.2 compatibility
local current_title_text, current_namespace, sets -- Defined when needed.
local namespaces = mw.site.namespaces
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function decode_entities(...)
decode_entities = require(string_utilities_module).decode_entities
return decode_entities(...)
end
local function extend(...)
extend = require(table_module).extend
return extend(...)
end
local function finalize_set(...)
finalize_set = require(parameters_finalize_set_module)
return finalize_set(...)
end
local function get_family_by_code(...)
get_family_by_code = require(families_module).getByCode
return get_family_by_code(...)
end
local function get_family_by_name(...)
get_family_by_name = require(families_module).getByCanonicalName
return get_family_by_name(...)
end
local function get_language_by_code(...)
get_language_by_code = require(languages_module).getByCode
return get_language_by_code(...)
end
local function get_language_by_name(...)
get_language_by_name = require(languages_module).getByCanonicalName
return get_language_by_name(...)
end
local function get_script_by_code(...)
get_script_by_code = require(scripts_module).getByCode
return get_script_by_code(...)
end
local function get_script_by_name(...)
get_script_by_name = require(scripts_module).getByCanonicalName
return get_script_by_name(...)
end
local function get_wm_lang_by_code(...)
get_wm_lang_by_code = require(wikimedia_languages_module).getByCode
return get_wm_lang_by_code(...)
end
local function get_wm_lang_by_code_with_fallback(...)
get_wm_lang_by_code_with_fallback = require(wikimedia_languages_module).getByCodeWithFallback
return get_wm_lang_by_code_with_fallback(...)
end
local function gsplit(...)
gsplit = require(string_utilities_module).gsplit
return gsplit(...)
end
local function is_callable(...)
is_callable = require(functions_module).is_callable
return is_callable(...)
end
local function is_integer(...)
is_integer = require(math_module).is_integer
return is_integer(...)
end
local function is_internal_title(...)
is_internal_title = require(pages_module).is_internal_title
return is_internal_title(...)
end
local function is_positive_integer(...)
is_positive_integer = require(math_module).is_positive_integer
return is_positive_integer(...)
end
local function iterate_list(...)
iterate_list = require(table_module).iterateList
return iterate_list(...)
end
local function num_keys(...)
num_keys = require(table_module).numKeys
return num_keys(...)
end
local function parse_gender_and_number_spec(...)
parse_gender_and_number_spec = require(gender_and_number_utilities_module).parse_gender_and_number_spec
return parse_gender_and_number_spec(...)
end
local function parse_references(...)
parse_references = require(references_module).parse_references
return parse_references(...)
end
local function pattern_escape(...)
pattern_escape = require(string_utilities_module).pattern_escape
return pattern_escape(...)
end
local function php_trim(...)
php_trim = require(scribunto_module).php_trim
return php_trim(...)
end
local function scribunto_parameter_key(...)
scribunto_parameter_key = require(scribunto_module).scribunto_parameter_key
return scribunto_parameter_key(...)
end
local function sort(...)
sort = require(collation_module).sort
return sort(...)
end
local function sorted_pairs(...)
sorted_pairs = require(table_module).sortedPairs
return sorted_pairs(...)
end
local function split_labels_on_comma(...)
split_labels_on_comma = require(labels_module).split_labels_on_comma
return split_labels_on_comma(...)
end
local function split_on_comma(...)
split_on_comma = require(parse_utilities_module).split_on_comma
return split_on_comma(...)
end
local function tonumber_extended(...)
tonumber_extended = require(math_module).tonumber_extended
return tonumber_extended(...)
end
local function track(...)
track = require(parameters_track_module)
return track(...)
end
local function yesno(...)
yesno = require(yesno_module)
return yesno(...)
end
--[==[ intro:
This module is used to standardize template argument processing and checking. A typical workflow is as follows (based
on [[Module:translations]]):
{
...
local parent_args = frame:getParent().args
local params = {
[1] = {required = true, type = "language", default = "und"},
[2] = true,
[3] = {list = true},
["alt"] = true,
["id"] = true,
["sc"] = {type = "script"},
["tr"] = true,
["ts"] = true,
["lit"] = true,
}
local args = require("Module:parameters").process(parent_args, params)
-- Do further processing of the parsed arguments in `args`.
...
}
The `params` table should have the parameter names as the keys, and a (possibly empty) table of parameter tags as the
value. An empty table as the value merely states that the parameter exists, but should not receive any special
treatment; if desired, empty tables can be replaced with the value `true` as a perforamnce optimization.
Possible parameter tags are listed below:
; {required = true}
: The parameter is required; an error is shown if it is not present. The template's page itself is an exception; no
error is shown there.
; {default =}
: Specifies a default input value for the parameter, if it is absent or empty. This will be processed as though it were
the input instead, so (for example) {default = "und"} with the type {"language"} will return a language object for
[[:Category:Undetermined language|Undetermined language]] if no language code is provided. When used on list
parameters, this specifies a default value for the first item in the list only. Note that it is not possible to
generate a default that depends on the value of other parameters. If used together with {required = true}, the default
applies only to template pages (see the following entry), as a side effect of the fact that "required" parameters
aren't actually required on template pages. This can be used to show an example of the template in action when the
template page is visited; however, it is preferred to use `template_default` for this purpose, for clarity.
; {template_default =}
: Specifies a default input value for absent or empty parameters only on template pages. Template pages are any page in
the template space (beginning with `Template:`) except for documentation pages (those ending in `.../documentation`).
This can be used to provide an example value for a non-required parameter when the template page is visited, without
interfering with other uses of the template. Both `template_default` and `default` can be specified for the same
parameter. If this is done, `template_default` applies on template pages, and `default` on other pages. As an example,
{{tl|cs-IPA}} uses the equivalent of {[1] = {default = "+", template_default = "příklad"}} to supply a default of
{"+"} for mainspace and documentation pages (which tells the module to use the value of the {{para|pagename}}
parameter, falling back to the actual pagename), but {"příklad"} (which means "example"), on [[Template:cs-IPA]].
; {alias_of =}
: Treat the parameter as an alias of another. When arguments are specified for this parameter, they will automatically
be renamed and stored under the alias name. This allows for parameters with multiple alternative names, while still
treating them as if they had only one name. The conversion-related properties of an aliased parameter (e.g. `type`,
`set`, `convert`, `sublist`) are taken from the aliasee, and the corrresponding properties set on the alias itself
are ignored; but other properties on the alias are taken from the alias's spec and not from the aliasee's spec. This
means, for example, that if you create an alias of a list parameter, the alias must also specify the `list` property
or it is not a list. (In such a case, a value specified for the alias goes into the first item of the aliasee's list.
You cannot make a list alias of a non-list parameter; this causes an error to be thrown.) Similarly, if you specify
`separate_no_index` on an aliasee but not on the alias, uses of the unindexed aliasee parameter are stored into the
`.default` key, but uses of the unindexed alias are stored into the first numbered key of the aliasee's list.
Aliases cannot be required, as this prevents the other name or names of the parameter from being used. Parameters
that are aliases and required at the same time cause an error to be thrown.
; {allow_empty = true}
: If the argument is an empty string value, it is not converted to {nil}, but kept as-is. The use of `allow_empty` is
disallowed if a type has been specified, and causes an error to be thrown.
; {no_trim = true}
: Spacing characters such as spaces and newlines at the beginning and end of a positional parameter are not removed.
(MediaWiki itself automatically trims spaces and newlines at the edge of named parameters.) The use of `no_trim` is
disallowed if a type has been specified, and causes an error to be thrown.
; {type =}
: Specifies what value type to convert the argument into. The default is to leave it as a text string. Alternatives are:
:; {type = "boolean"}
:: The value is treated as a boolean value, either true or false. No value, the empty string, and the strings {"0"},
{"no"}, {"n"}, {"false"}, {"f"} and {"off"} are treated as {false}, all other values are considered {true}.
:; {type = "number"}
:: The value is converted into a number, and throws an error if the value is not parsable as a number. Input values may
be signed (`+` or `-`), and may contain decimal points and leading zeroes. If {allow_hex = true}, then hexadecimal
values in the form {"0x100"} may optionally be used instead, which otherwise have the same syntax restrictions
(including signs, decimal digits, and leading zeroes after {"0x"}). Hexadecimal inputs are not case-sensitive. Lua's
special number values (`inf` and `nan`) are not possible inputs.
:; {type = "range"}
:: The value is interpreted as a hyphen-separated range of two numbers (e.g. {"2-4"} is interpreted as the range from
{2} to {4}). A number input without a hyphen is interpreted as a range from that number to itself (e.g. the input {"1"} is interpreted as the range from {1} to {1}). Any optional flags which are available for numbers will also work for ranges.
:; {type = "language"}
:: The value is interpreted as a full or [[Wiktionary:Languages#Etymology-only languages|etymology-only language]] code
language code (or name, if {method = "name"}) and converted into the corresponding object (see [[Module:languages]]).
If the code or name is invalid, then an error is thrown. The additional setting {family = true} can be given to allow
[[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
Note that to distinguish an etymology-only language object from a full language object, use
{object:hasType("language", "etymology-only")}.
:; {type = "full language"}
:: The value is interpreted as a full language code (or name, if {method = "name"}) and converted into the corresponding
object (see [[Module:languages]]). If the code or name is invalid, then an error is thrown. Etymology-only languages
are not allowed. The additional setting {family = true} can be given to allow
[[Wiktionary:Language families|language family codes]] to be considered valid and the corresponding object returned.
:; {type = "Wikimedia language"}
:: The value is interpreted as a code and converted into a Wikimedia language object. If the code is invalid, then an
error is thrown. If {fallback = true} is specified, conventional language codes which are different from their
Wikimedia equivalent will also be accepted as a fallback.
:; {type = "family"}
:: The value is interpreted as a language family code (or name, if {method = "name"}) and converted into the
corresponding object (see [[Module:families]]). If the code or name is invalid, then an error is thrown.
:; {type = "script"}
:: The value is interpreted as a script code (or name, if {method = "name"}) and converted into the corresponding object
(see [[Module:scripts]]). If the code or name is invalid, then an error is thrown.
:; {type = "title"}
:: The value is interpreted as a page title and converted into the corresponding object (see the
[[mw:Extension:Scribunto/Lua_reference_manual#Title_library|Title library]]). If the page title is invalid, then an
error is thrown; by default, external titles (i.e. those on other wikis) are not treated as valid. Options are:
::; {namespace = n}
::: The default namespace, where {n} is a namespace number; this is treated as {0} (the mainspace) if not specified.
::; {allow_external = true}
::: External titles are treated as valid.
::; {prefix = "namespace override"} (default)
::: The default namespace prefix will be prefixed to the value is already prefixed by a namespace prefix. For instance,
the input {"Foo"} with namespace {10} returns {"Template:Foo"}, {"Wiktionary:Foo"} returns {"Wiktionary:Foo"}, and
{"Template:Foo"} returns {"Template:Foo"}. Interwiki prefixes cannot act as overrides, however: the input {"fr:Foo"}
returns {"Template:fr:Foo"}.
::; {prefix = "force"}
::: The default namespace prefix will be prefixed unconditionally, even if the value already appears to be prefixed.
This is the way that {{tl|#invoke:}} works when calling modules from the module namespace ({828}): the input {"Foo"}
returns {"Module:Foo"}, {"Wiktionary:Foo"} returns {"Module:Wiktionary:Foo"}, and {"Module:Foo"} returns
{"Module:Module:Foo"}.
::; {prefix = "full override"}
::: The same as {prefix = "namespace override"}, except that interwiki prefixes can also act as overrides. For instance,
{"el:All topics"} with namespace {14} returns {"el:Category:All topics"}. Due to the limitations of MediaWiki, only
the first prefix in the value may act as an override, so the namespace cannot be overridden if the first prefix is
an interwiki prefix: e.g. {"el:Template:All topics"} with namespace {14} returns {"el:Category:Template:All topics"}.
:; {type = "parameter"}
:: The value is interpreted as the name of a parameter, and will be normalized using the method that Scribunto uses when
constructing a {frame.args} table of arguments. This means that integers will be converted to numbers, but all other
arguments will remain as strings (e.g. {"1"} will be normalized to {1}, but {"foo"} and {"1.5"} will remain
unchanged). Note that Scribunto also trims parmeter names, following the same trimming method that this module
applies by default to all parameter types.
:: This type is useful when one set of input arguments is used to construct a {params} table for use in a subsequent
{export.process()} call with another set of input arguments; for instance, the set of valid parameters for a template
might be defined as {{tl|#invoke:[some module]|args=}} in the template, where {args} is a sublist of valid parameters
for the template.
:; {type = "qualifier"}
:: The value is interpreted as a qualifier and converted into the correct format for passing into `format_qualifiers()`
in [[Module:qualifier]] (which currently just means converting it to a one-item list).
:; {type = "labels"}
:: The value is interpreted as a comma-separated list of labels and converted into the correct format for passing into
`show_labels()` in [[Module:labels]] (which is currently a list of strings). Splitting is done on commas not followed
by whitespace, except that commas inside of double angle brackets do not count even if not followed by whitespace.
This type should be used by for normal labels (typically specified using {{para|l}} or {{para|ll}}) and accent
qualifiers (typically specified using {{para|a}} and {{para|aa}}).
:; {type = "references"}
:: The value is interpreted as one or more references, in the format prescribed by `parse_references()` in
[[Module:references]], and converted into a list of objects of the form accepted by `format_references()` in the same
module. If a syntax error is found in the reference format, an error is thrown.
:; {type = "genders"}
:: The value is interpreted as one or more comma-separated gender/number specs, in the format prescribed by
[[Module:gender and number]]. Inline modifiers (`<q:...>`, `<qq:...>`, `<l:...>`, `<ll:...>` or `<ref:...>`) may be
attached to a gender/number spec.
:; {type = function(val) ... end}
:: `type` may be set to a function (or callable table), which must take the argument value as its sole argument, and must
output one of the other recognized types. This is particularly useful for lists (see below), where certain values need
to be interpreted differently to others.
; {list =}
: Treat the parameter as a list of values, each having its own parameter name, rather than a single value. The
parameters will have a number at the end, except optionally for the first (but see also {require_index = true}). For
example, {list = true} on a parameter named "head" will include the parameters {{para|head}} (or {{para|head1}}),
{{para|head2}}, {{para|head3}} and so on. If the parameter name is a number, another number doesn't get appended, but
the counting simply continues, e.g. for parameter {3} the sequence is {{para|3}}, {{para|4}}, {{para|5}} etc. List
parameters are returned as numbered lists, so for a template that is given the parameters `|head=a|head2=b|head3=c`,
the processed value of the parameter {"head"} will be { { "a", "b", "c" }}}.
: The value for {list =} can also be a string. This tells the module that parameters other than the first should have a
different name, which is useful when the first parameter in a list is a number, but the remainder is named. An example
would be for genders: {list = "g"} on a parameter named {1} would have parameters {{para|1}}, {{para|g2}}, {{para|g3}}
etc.
: If the number is not located at the end, it can be specified by putting {"\1"} at the number position. For example,
parameters {{para|f1accel}}, {{para|f2accel}}, ... can be captured by using the parameter name {"f\1accel"}, as is
done in [[Module:headword/templates]].
; {set =}
: Require that the value of the parameter be one of the specified list of values (or omitted, if {required = true} isn't
given). The values in the specified list should be strings corresponding to the raw parameter values except when
{type = "number"}, in which case they should be numbers. A individual value in the list can also be an ''alias list'',
which is a list where the first value is the "canonical" value and the remainder are aliases. When one of the aliases
is used, the resulting parameter field in the returned arguments structure will have the canonical value. The use of
`set` is disallowed if {type = "boolean"} and causes an error to be thrown.
; {sublist =}
: The value of the parameter is a delimiter-separated list of individual raw values. The resulting field in `args` will
be a Lua list (i.e. a table with numeric indices) of the converted values. If {sublist = true} is given, the values
will be split on commas (possibly with whitespace on one or both sides of the comma, which is ignored). If
{sublist = "comma without whitespace"} is given, the values will be split on commas which are not followed by whitespace,
and which aren't preceded by an escaping backslash. Otherwise, the value of `sublist` should be either a Lua pattern
specifying the delimiter(s) to split on or a function (or callable table) to do the splitting, which is passed two values
(the value to split and a function to signal an error) and should return a list of the split values.
; {convert =}
: If given, this specifies a function (or callable table) to convert the raw parameter value into the Lua object used
during further processing. The function is passed two arguments, the raw parameter value itself and a function used to
signal an error during parsing or conversion, and should return one value, the converted parameter. The error-signaling
function contains the name and raw value of the parameter embedded into the message it generates, so these do not need to
specified in the message passed into it. If `type` is specified in conjunction with `convert`, the processing by
`type` happens first. If `sublist` is given in conjunction with `convert`, the raw parameter value will be split
appropriately and `convert` called on each resulting item.
; {allow_hex = true}
: When used in conjunction with {type = "number"}, allows hexadecimal numbers as inputs, in the format {"0x100"} (which is
not case-sensitive).
; {family = true}
: When used in conjunction with {type = "language"}, allows [[Wiktionary:Language families|language family codes]] to be
returned. To check if a given object refers to a language family, use {object:hasType("family")}.
; {method = "name"}
: When used in conjunction with {type = "language"}, {type = "family"} or {type = "script"}, checks for and parses a
language, family or script name instead of a code.
; {allow_holes = true}
: This is used in conjunction with list-type parameters. By default, the values are tightly packed in the resulting
list. This means that if, for example, an entry specified `head=a|head3=c` but not {{para|head2}}, the returned list
will be { {"a", "c"}}}, with the values stored at the indices {1} and {2}, not {1} and {3}. If it is desirable to keep
the numbering intact, for example if the numbers of several list parameters correlate with each other (like those of
{{tl|affix}}), then this tag should be specified.
: If {allow_holes = true} is given, there may be {nil} values in between two real values, which makes many of Lua's
table processing functions no longer work, like {#} or {ipairs()}. To remedy this, the resulting table will contain an
additional named value, `maxindex`, which tells you the highest numeric index that is present in the table. In the
example above, the resulting table will now be { { "a", nil, "c", maxindex = 3}}}. That way, you can iterate over the
values from {1} to `maxindex`, while skipping {nil} values in between.
; {disallow_holes = true}
: This is used in conjunction with list-type parameters. As mentioned above, normally if there is a hole in the source
arguments, e.g. `head=a|head3=c` but not {{para|head2}}, it will be removed in the returned list. If
{disallow_holes = true} is specified, however, an error is thrown in such a case. This should be used whenever there
are multiple list-type parameters that need to line up (e.g. both {{para|head}} and {{para|tr}} are available and
{{para|head3}} lines up with {{para|tr3}}), unless {allow_holes = true} is given and you are prepared to handle the
holes in the returned lists.
; {disallow_missing = true}
: This is similar to {disallow_holes = true}, but an error will not be thrown if an argument is blank, rather than
completely missing. This may be used to tolerate intermediate blank numerical parameters, which sometimes occur in list
templates. For instance, `head=a|head2=|head3=c` will not throw an error, but `head=a|head3=c` will.
; {require_index = true}
: This is used in conjunction with list-type parameters. By default, the first parameter can have its index omitted.
For example, a list parameter named `head` can have its first parameter specified as either {{para|head}} or
{{para|head1}}. If {require_index = true} is specified, however, only {{para|head1}} is recognized, and {{para|head}}
will be treated as an unknown parameter. {{tl|affixusex}} (and variants {{tl|suffixusex}}, {{tl|prefixusex}}) use
this, for example, on all list parameters.
; {separate_no_index = true}
: This is used to distinguish between {{para|head}} and {{para|head1}} as different parameters. For example, in
{{tl|affixusex}}, to distinguish between {{para|sc}} (a script code for all elements in the usex's language) and
{{para|sc1}} (the script code of the first element, used when the first element is prefixed with a language code to
indicate that it is in a different language). When this is used, the resulting table will contain an additional named
value, `default`, which contains the value for the indexless argument.
; {flatten = true}
: This is used in conjunction with list-type parameters when `sublist` or a list-generating type such as {"labels"} or
{"genders"} is also specified, and causes the resulting list to be flattened. Not currently compatible with
{allow_holes = true}.
; {replaced_by =}
: Specifies that the parameter is no longer valid, and has been replaced by some other mechanism. If the value of
`replaced_by` is a string, it is the name of the new parameter to use instead. Use the `reason` tag to specify the
reason why this change has been made, e.g.
{reason = "for consistency with the corresponding parameter in other Romance-language headword templates"}. If the
value of `replaced_by` is {false}, there is no replacement parameter. In this case, `instead` should be supplied
with a description of what to do instead, e.g.
{instead = "use an inline modifier on |2= such as <q:...>, <qq:...>, <l:...> or <ll:...>"}. You can also supply a
justification in `reason` if you feel it is appropriate or necessary to do so.
; {reason =}
: When used in conjunction with `replaced_by`, specifies the reason for the parameter replacement.
; {instead =}
: When used in conjunction with {replaced_by = false}, specifies what to do instead of using the removed parameter.
; {demo = true}
: This is used as a way to ensure that the parameter is only enabled on the template's own page (and its documentation
page), and in the User: namespace; otherwise, it will be treated as an unknown parameter. This should only be used if
special settings are required to showcase a template in its documentation (e.g. adjusting the pagename or disabling
categorization). In most cases, it should be possible to do this without using demo parameters, but they may be
required if a template/documentation page also contains real uses of the same template as well (e.g. {{tl|shortcut}}),
as a way to distinguish them.
; {deprecated = true}
: This is for tracking the use of deprecated parameters, including any aliases that are being brought out of use. See
[[Wiktionary:Tracking]] for more information.
]==]
-- Returns true if the current page is a template or module containing the current {{#invoke}}.
-- If the include_documentation argument is given, also returns true if the current page is either page's docuemntation page.
local own_page, own_page_or_documentation
local function is_own_page(include_documentation)
if own_page == nil then
if current_namespace == nil then
local current_title = mw_title.getCurrentTitle()
current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
end
local frame = current_namespace == 828 and mw.getCurrentFrame() or
current_namespace == 10 and mw.getCurrentFrame():getParent()
if frame then
local frame_title_text = frame:getTitle()
own_page = current_title_text == frame_title_text
own_page_or_documentation = own_page or current_title_text == frame_title_text .. "/documentation"
else
own_page, own_page_or_documentation = false, false
end
end
return include_documentation and own_page_or_documentation or own_page
end
-------------------------------------- Some helper functions -----------------------------
-- Convert a list in `list` to a string, separating the final element from the preceding one(s) by `conjunction`. If
-- `dump_vals` is given, pass all values in `list` through mw.dumpObject() (WARNING: this destructively modifies
-- `list`). This is similar to serialCommaJoin() in [[Module:table]] when used with the `dontTag = true` option, but
-- internally uses mw.text.listToText().
local function concat_list(list, conjunction, dump_vals)
if dump_vals then
for k, v in pairs(list) do
list[k] = dump(v)
end
end
return list_to_text(list, nil, conjunction)
end
-- A helper function for use with generating error-signaling functions in the presence of raw value conversion. Format a
-- message `msg`, including the processed value `processed` if it is different from the raw value `rawval`; otherwise,
-- just return `msg`.
local function msg_with_processed(msg, rawval, processed)
if rawval == processed then
return msg
end
local processed_type = type(processed)
return format("%s (processed value %s)",
msg, (processed_type == "string" or processed_type == "number") and processed or dump(processed)
)
end
-------------------------------------- Error handling -----------------------------
local function process_error(fmt, ...)
local args = {...}
for i, val in ipairs(args) do
args[i] = dump(val)
end
if type(fmt) == "table" then
-- hacky signal that we're called from internal_process_error(), and not to omit stack frames
return error(format(fmt[1], unpack(args)))
end
return error(format(fmt, unpack(args)), 3)
end
local function internal_process_error(fmt, ...)
process_error({"Internal error in `params` table: " .. fmt}, ...)
end
-- Check that a parameter or argument is in the form form Scribunto normalizes input argument keys into (e.g. 1 not "1", "foo" not " foo "). Otherwise, it won't be possible to normalize inputs in the expected way. Unless is_argument is set, also check that the name only contains one placeholder at most, and that strings don't resolve to numeric keys once the placeholder has been substituted.
local function validate_name(name, desc, extra_name, is_argument)
local normalized = scribunto_parameter_key(name)
if name and name == normalized then
if is_argument or type(name) ~= "string" then
return
end
local placeholder = find(name, "\1", nil, true)
if not placeholder then
return
elseif find(name, "\1", placeholder + 1, true) then
error(format(
"Internal error: expected %s to only contain one placeholder, but saw %s",
extra_name and (desc .. dump(extra_name)) or desc, dump(name)
))
end
local first_name = gsub(name, "\1", "1")
normalized = scribunto_parameter_key(first_name)
if first_name == normalized then
return
end
error(format(
"Internal error: %s cannot resolve to numeric parameters once any placeholder has been substituted, but %s resolves to %s",
extra_name and (desc .. dump(extra_name)) or desc, dump(name), dump(normalized)
))
elseif normalized == nil then
error(format(
"Internal error: expected %s to be of type string or number, but saw %s",
extra_name and (desc .. dump(extra_name)) or desc, type(name)
))
end
error(format(
"Internal error: expected %s to be Scribunto-compatible: %s (a %s) should be %s (a %s)",
extra_name and (desc .. dump(extra_name)) or desc, dump(name), type(name), dump(normalized), type(normalized)
))
end
local function validate_alias_options(...)
local invalid = {
required = true,
default = true,
template_default = true,
allow_holes = true,
disallow_holes = true,
disallow_missing = true,
}
function validate_alias_options(param, name, main_param, alias_of)
for k in pairs(param) do
if invalid[k] then
track("bad alias option")
-- internal_process_error(
-- "parameter %s cannot have the option %s, as it is an alias of parameter %s.",
-- name, option, alias_of
-- )
end
end
-- Soon, aliases will inherit options from the main parameter via __index. Track cases where this would happen.
if main_param ~= true then
for k in pairs(main_param) do
if param[k] == nil and not invalid[k] then
if k == "list" then -- these need to be changed to list = false to retain current behaviour
track("mismatched list alias option")
elseif not (k == "type" or k == "set" or k == "sublist") then -- rarely specified on aliases, as they're effectively inherited already
track("mismatched alias option")
end
end
end
end
end
validate_alias_options(...)
end
-- TODO: give ranges instead of long lists, if possible.
local function params_list_error(params, msg)
local list, n = {}, 0
for name in sorted_pairs(params) do
n = n + 1
list[n] = name
end
error(format(
"Parameter%s %s.",
format(n == 1 and " %s is" or "s %s are", concat_list(list, " and ", true)),
msg
), 3)
end
-- Helper function for use with convert_val_error(). Format a list of possible choices using `concat_list` and
-- conjunction "or", displaying "either " before the choices if there's more than one.
local function format_choice_list(valid)
return (#valid > 1 and "either " or "") .. concat_list(valid, " or ")
end
-- Signal an error for a value `val` that is not of the right type `valid` (which is either a string specifying a type, or
-- a list of possible values, in the case where `set` was used). `name` is the name of the parameter and can be a
-- function to signal an error (which is assumed to automatically display the parameter's name and value). `seetext` is
-- an optional additional explanatory link to display (e.g. [[WT:LOL]], the list of possible languages and codes).
local function convert_val_error(val, name, valid, seetext)
if is_callable(name) then
if type(valid) == "table" then
valid = "choice, must be " .. format_choice_list(valid)
end
name(format("Invalid %s; the value %s is not valid%s", valid, val, seetext and "; see " .. seetext or ""))
else
if type(valid) == "table" then
valid = format_choice_list(valid)
else
valid = "a valid " .. valid
end
error(format("Parameter %s must be %s; the value %s is not valid.%s", dump(name), valid, dump(val),
seetext and " See " .. seetext .. "." or ""))
end
end
-- Generate the appropriate error-signaling function given parameter value `val` and name `name`. If `name` is already
-- a function, it is just returned; otherwise a function is generated and returned that displays the passed-in messaeg
-- along with the parameter's name and value.
local function make_parse_err(val, name)
if is_callable(name) then
return name
end
return function(msg)
error(format("%s: parameter %s=%s", msg, name, val))
end
end
-------------------------------------- Value conversion -----------------------------
-- For a list parameter `name` and corresponding value `list_name` of the `list` field (which should have the same value
-- as `name` if `list = true` was given), generate a pattern to match parameters of the list and store the pattern as a
-- key in `patterns`, with corresponding value set to `name`. For example, if `list_name` is "tr", the pattern will
-- match "tr" as well as "tr1", "tr2", ..., "tr10", "tr11", etc. If the `list_name` contains a \1 in it, the numeric
-- portion goes in place of the \1. For example, if `list_name` is "f\1accel", the pattern will match "faccel",
-- "f1accel", "f2accel", etc. Any \1 in `name` is removed before storing into `patterns`.
local function save_pattern(name, list_name, patterns)
name = type(name) == "string" and gsub(name, "\1", "") or name
if find(list_name, "\1", nil, true) then
patterns["^" .. gsub(pattern_escape(list_name), "\1", "([1-9]%%d*)") .. "$"] = name
else
patterns["^" .. pattern_escape(list_name) .. "([1-9]%d*)$"] = name
list_name = list_name .. "\1"
end
validate_name(list_name, "the list field of parameter ", name)
return patterns
end
-- A helper function for use with `sublist`. It is an iterator function for use in a for-loop that returns split
-- elements of `val` using `sublist` (a Lua split pattern; boolean `true` to split on commas optionally surrounded by
-- whitespace; "comma without whitespace" to split only on commas not followed by whitespace which have not been escaped
-- by a backslash; or a function to do the splitting, which is passed two values, the value to split and a function to
-- signal an error, and should return a list of the split elements). `name` is the parameter name or error-signaling
-- function passed into convert_val().
local function split_sublist(val, name, sublist)
if sublist == true then
return gsplit(val, "%s*,%s*")
-- Split an argument on comma, but not comma followed by whitespace.
elseif sublist == "comma without whitespace" then
-- If difficult cases, use split_on_comma.
if find(val, "\\", nil, true) or match(val, ",%s") then
return iterate_list(split_on_comma(val))
end
-- Otherwise, use gsplit.
return gsplit(val, ",")
elseif type(sublist) == "string" then
return gsplit(val, sublist)
elseif not is_callable(sublist) then
error(format('Internal error: expected `sublist` to be of type "string" or "function" or boolean `true`, but saw %s', dump(sublist)))
end
return iterate_list(sublist(val, make_parse_err(val, name)))
end
-- For parameter named `name` with value `val` and param spec `param`, if the `set` field is specified, verify that the
-- value is one of the one specified in `set`, and throw an error otherwise. `name` is taken directly from the
-- corresponding parameter passed into convert_val() and may be a function to signal an error. Optional `param_type` is
-- a string specifying the conversion type of `val` and is used for special-casing: If `param_type` is "boolean", an
-- internal error is thrown (since `set` cannot be used in conjunction with booleans) and if `param_type` is "number",
-- no checking happens because in this case `set` contains numbers and is checked inside the number conversion function
-- itself, after converting `val` to a number. Return the canonical value of `val` (which may be different from `val`
-- if an alias map is given).
local function check_set(val, name, param, param_type)
if param_type == "boolean" then
error(format('Internal error: cannot use `set` with `type = "%s"`', param_type))
-- Needs to be special cased because the check happens after conversion to numbers.
elseif param_type == "number" then
return val
end
local set, map = param.set
if sets == nil then
map = finalize_set(set, name)
sets = {[set] = map}
else
map = sets[set]
if map == nil then
map = finalize_set(set, name)
sets[set] = map
end
end
local newval = map[val]
if newval == true then
return val
elseif newval ~= nil then
return newval
end
local list = {}
for k, v in sorted_pairs(map) do
if v == true then
insert(list, dump(k))
else
insert(list, ("%s (alias of %s)"):format(dump(k), dump(v)))
end
end
-- If the parameter is not required then put "or empty" at the end of the list, to avoid implying the parameter is actually required.
if not param.required then
insert(list, "empty")
end
convert_val_error(val, name, list)
end
local function convert_language(val, name, param, allow_etym)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_language_by_code, "code"
elseif method == "name" then
func, method = get_language_by_name, "name"
else
error(format('Internal error: expected `method` for type `language` to be "code", "name" or undefined, but saw %s', dump(method)))
end
local lang = func(val, nil, allow_etym, param.family)
if lang then
return lang
end
local list, links = {"language"}, {"[[WT:LOL]]"}
if allow_etym then
insert(list, "etymology language")
insert(links, "[[WT:LOL/E]]")
end
if param.family then
insert(list, "family")
insert(links, "[[WT:LOF]]")
end
convert_val_error(val, name, concat_list(list, " or ") .. " " .. (method == "name" and "name" or "code"), concat_list(links, " and "))
end
local function convert_number(val, allow_hex)
-- Call tonumber_extended with the `real_finite` flag, which filters out ±infinity and NaN.
-- By default, specify base 10, which prevents 0x hex inputs from being converted.
-- If `allow_hex` is set, then don't give a base, which means 0x hex inputs will work.
local num = tonumber_extended(val, not allow_hex and 10 or nil, "finite_real")
if not num then
return num
end
if match(val, "[eEpP.]") then -- float
track("number not an integer")
end
if find(val, "+", nil, true) then
track("number with +")
end
-- Track various unusual number inputs to determine if it should be restricted to positive integers by default (possibly including 0).
if not is_positive_integer(num) then
track("number not a positive integer")
if num == 0 then
track("number is 0")
elseif not is_integer(num) then
track("number not an integer")
end
end
return num
end
-- TODO: validate parameter specs separately, as it's making the handler code really messy at the moment.
local type_handlers = setmetatable({
["boolean"] = function(val)
return yesno(val, true)
end,
["family"] = function(val, name, param)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_family_by_code, "code"
elseif method == "name" then
func, method = get_family_by_name, "name"
else
error(format('Internal error: expected `method` for type `family` to be "code", "name" or undefined, but saw %s', dump(method)))
end
return func(val) or convert_val_error(val, name, "family " .. method, "[[WT:LOF]]")
end,
["labels"] = function(val, name, param)
-- FIXME: Should be able to pass in a parse_err function.
return split_labels_on_comma(val)
end,
["language"] = function(val, name, param)
return convert_language(val, name, param, true)
end,
["full language"] = convert_language,
["number"] = function(val, name, param)
local allow_hex = param.allow_hex
if allow_hex and allow_hex ~= true then
error(format(
'Internal error: expected `allow_hex` for type `number` to be of type "boolean" or undefined, but saw %s',
dump(allow_hex)
))
end
local num = convert_number(val, allow_hex)
if param.set then
-- Don't pass in "number" here; otherwise no checking will happen.
num = check_set(num, name, param)
end
if num then
return num
end
convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number")
end,
["range"] = function(val, name, param)
local allow_hex = param.allow_hex
if allow_hex and allow_hex ~= true then
error(format(
'Internal error: expected `allow_hex` for type `range` to be of type "boolean" or undefined, but saw %s',
dump(allow_hex)
))
end
-- Pattern ensures leading minus signs are accounted for.
local m1, m2 = match(val, "^(%s*%S.-)%-(%s*%S.*)")
if m1 then
m1 = convert_number(m1, allow_hex)
if m1 then
m2 = convert_number(m2, allow_hex)
if m2 then
return {m1, m2}
end
end
end
-- Try `val` if it couldn't be split into a range, and return a range of `val` to `val` if possible.
local num = convert_number(val, allow_hex)
if num then
return {num, num}
end
convert_val_error(val, name, (allow_hex and "decimal or hexadecimal " or "") .. "number or a hyphen-separated range of two numbers")
end,
["parameter"] = function(val, name, param)
-- Use the `no_trim` option, as any trimming will have already been done.
return scribunto_parameter_key(val, true)
end,
["qualifier"] = function(val, name, param)
return {val}
end,
["references"] = function(val, name, param)
return parse_references(val, make_parse_err(val, name))
end,
["genders"] = function(val, name, param)
if not val:find("[,<]") then
return {{spec = val}}
end
-- NOTE: We don't pass in allow_space_around_comma. Consistent with other comma-separated types, there shouldn't
-- be spaces around the comma.
return parse_gender_and_number_spec {
spec = val,
parse_err = make_parse_err(val, name),
allow_multiple = true,
}
end,
["script"] = function(val, name, param)
local method, func = param.method
if method == nil or method == "code" then
func, method = get_script_by_code, "code"
elseif method == "name" then
func, method = get_script_by_name, "name"
else
error(format('Internal error: expected `method` for type `script` to be "code", "name" or undefined, but saw %s', dump(method)))
end
return func(val) or convert_val_error(val, name, "script " .. method, "[[WT:LOS]]")
end,
["string"] = function(val, name, param) -- To be removed as unnecessary.
track("string")
return val
end,
-- TODO: add support for resolving to unsupported titles.
-- TODO: split this into "page name" (i.e. internal) and "link target" (i.e. external as well), which is more intuitive.
["title"] = function(val, name, param)
local namespace = param.namespace
if namespace == nil then
namespace = 0
else
local valid_type = type(namespace) ~= "number" and 'of type "number" or undefined' or
not namespaces[namespace] and "a valid namespace number" or
nil
if valid_type then
error(format('Internal error: expected `namespace` for type `title` to be %s, but saw %s', valid_type, dump(namespace)))
end
end
-- Decode entities. WARNING: mw.title.makeTitle must be called with `decoded` (as it doesn't decode) and mw.title.new must be called with `val` (as it does decode, so double-decoding needs to be avoided).
local decoded, prefix, title = decode_entities(val), param.prefix
-- If the input is a fragment, treat the title as the current title with the input fragment.
if sub(decoded, 1, 1) == "#" then
-- If prefix is "force", only get the current title if it's in the specified namespace. current_title includes the namespace prefix.
if current_namespace == nil then
local current_title = mw_title.getCurrentTitle()
current_title_text, current_namespace = current_title.prefixedText, current_title.namespace
end
if not (prefix == "force" and namespace ~= current_namespace) then
title = new_title(current_title_text .. val)
end
elseif prefix == "force" then
-- Unconditionally add the namespace prefix (mw.title.makeTitle).
title = make_title(namespace, decoded)
elseif prefix == "full override" then
-- The first input prefix will be used as an override (mw.title.new). This can be a namespace or interwiki prefix.
title = new_title(val, namespace)
elseif prefix == nil or prefix == "namespace override" then
-- Only allow namespace prefixes to override. Interwiki prefixes therefore need to be treated as plaintext (e.g. "el:All topics" with namespace 14 returns "el:Category:All topics", but we want "Category:el:All topics" instead; if the former is really needed, then the input ":el:Category:All topics" will work, as the initial colon overrides the namespace). mw.title.new can take namespace names as well as numbers in the second argument, and will throw an error if the input isn't a valid namespace, so this can be used to determine if a prefix is for a namespace, since mw.title.new will return successfully only if there's either no prefix or the prefix is for a valid namespace (in which case we want the override).
local success
success, title = pcall(new_title, val, match(decoded, "^.-%f[:]") or namespace)
-- Otherwise, get the title with mw.title.makeTitle, which unconditionally adds the namespace prefix, but behaves like mw.title.new if the namespace is 0.
if not success then
title = make_title(namespace, decoded)
end
else
error(format('Internal error: expected `prefix` for type `title` to be "force", "full override", "namespace override" or undefined, but saw %s', dump(prefix)))
end
local allow_external = param.allow_external
if allow_external == true then
return title or convert_val_error(val, name, "Wiktionary or external page title")
elseif not allow_external then
return title and is_internal_title(title) and title or convert_val_error(val, name, "Wiktionary page title")
end
error(format('Internal error: expected `allow_external` for type `title` to be of type "boolean" or undefined, but saw %s', dump(allow_external)))
end,
["Wikimedia language"] = function(val, name, param)
local fallback = param.fallback
if fallback == true then
return get_wm_lang_by_code_with_fallback(val) or convert_val_error(val, name, "Wikimedia language or language code")
elseif not fallback then
return get_wm_lang_by_code(val) or convert_val_error(val, name, "Wikimedia language code")
end
error(format('Internal error: expected `fallback` for type `Wikimedia language` to be of type "boolean" or undefined, but saw %s', dump(fallback)))
end,
}, {
-- TODO: decode HTML entities in all input values. Non-trivial to implement, because we need to avoid any downstream functions decoding the output from this module, which would be double-decoding. Note that "title" has this implemented already, and it needs to have both the raw input and the decoded input to avoid double-decoding by me.title.new, so any implementation can't be as simple as decoding in __call then passing the result to the handler.
__call = function(self, val, name, param, param_type, default)
local val_type = type(val)
-- TODO: check this for all possible parameter types.
if val_type == param_type then
return val
elseif val_type ~= "string" then
local expected = "string"
if default and (param_type == "boolean" or param_type == "number") then
expected = param_type .. " or " .. expected
end
error(format(
"Internal error: %sargument %s has the type %s; expected a %s.",
default and (default .. " for ") or "", name, dump(val_type), expected
))
end
local func = self[param_type]
if func == nil then
error(format("Internal error: %s is not a recognized parameter type.", dump(param_type)))
end
return func(val, name, param)
end
})
--[==[ func: export.convert_val(val, name, param)
Convert a parameter value according to the associated specs listed in the `params` table passed to
[[Module:parameters]]. `val` is the value to convert for a parameter whose name is `name` (used only in error messages).
`param` is the spec (the value part of the `params` table for the parameter). In place of passing in the parameter name,
`name` can be a function that throws an error, displaying the specified message along with the parameter name and value.
This function processes all the conversion-related fields in `param`, including `type`, `set`, `sublist`, `convert`,
etc. It returns the converted value.
]==]
local function convert_val(val, name, param, default)
local param_type = param.type or "string"
-- If param.type is a function, resolve it to a recognized type.
if is_callable(param_type) then
param_type = param_type(val)
end
local convert, sublist = param.convert, param.sublist
-- `val` might not be a string if it's the default value.
if sublist and type(val) == "string" then
local retlist, set = {}, param.set
if convert then
local thisindex, thisval, insval, parse_err = 0
if is_callable(name) then
-- We assume the passed-in error function in `name` already shows the parameter name and raw value.
function parse_err(msg)
name(format("%s: item #%s=%s",
msg_with_processed(msg, thisval, insval), thisindex, thisval)
)
end
else
function parse_err(msg)
error(format("%s: item #%s=%s of parameter %s=%s",
msg_with_processed(msg, thisval, insval), thisindex, thisval, name, val)
)
end
end
for v in split_sublist(val, name, sublist) do
thisindex, thisval = thisindex + 1, v
if set then
v = check_set(v, name, param, param_type)
end
insert(retlist, convert(type_handlers(v, name, param, param_type, default), parse_err))
end
else
for v in split_sublist(val, name, sublist) do
if set then
v = check_set(v, name, param, param_type)
end
insert(retlist, type_handlers(v, name, param, param_type, default))
end
end
return retlist
elseif param.set then
val = check_set(val, name, param, param_type)
end
local retval = type_handlers(val, name, param, param_type, default)
if convert then
local parse_err
if is_callable(name) then
-- We assume the passed-in error function in `name` already shows the parameter name and raw value.
if retval == val then
-- This is an optimization to avoid creating a closure. The second arm works correctly even
-- when retval == val.
parse_err = name
else
function parse_err(msg)
name(msg_with_processed(msg, val, retval))
end
end
else
function parse_err(msg)
error(format("%s: parameter %s=%s", msg_with_processed(msg, val, retval), name, val))
end
end
retval = convert(retval, parse_err)
end
-- If `sublist` is set but the input wasn't a string, return `retval` as a one-item list.
if sublist then
retval = {retval}
end
return retval
end
export.convert_val = convert_val -- used by [[Module:parameter utilities]]
local function unknown_param(name, val, args_unknown)
track("unknown parameters")
args_unknown[name] = val
return args_unknown
end
local function check_string_param_modifier(param_type, name, tag)
if param_type and not (param_type == "string" or param_type == "parameter" or is_callable(param_type)) then
internal_process_error(
"%s cannot be set unless %s is set to %s (the default), %s or a function: parameter %s has the type %s.",
tag, "type", "string", "parameter", name, param_type
)
end
end
local function hole_error(params, name, listname, this, nxt, extra)
-- `process_error` calls `dump` on values to be inserted into
-- error messages, but with numeric lists this causes "numeric"
-- to look like the name of the list rather than a description,
-- as `dump` adds quote marks. Insert it early to avoid this,
-- but add another %s specifier in all other cases, so that
-- actual list names will be displayed properly.
local offset, specifier, starting_from = 0, "%s", ""
local msg = "Item %%d in the list of %s parameters must be given if item %%d is given, because %sthere shouldn't be any gaps due to missing%s parameters."
local specs = {}
if type(listname) == "string" then
specs[2] = listname
elseif type(name) == "number" then
offset = name - 1 -- To get the original parameter.
specifier = "numeric"
-- If the list doesn't start at parameter 1, avoid implying
-- there can't be any gaps in the numeric parameters if
-- some parameter with a lower key is optional.
for j = name - 1, 1, -1 do
local _param = params[j]
if not (_param and _param.required) then
starting_from = format("(starting from parameter %d) ", dump(j + 1))
break
end
end
else
specs[2] = name
end
specs[1] = this + offset -- Absolute index for this item.
insert(specs, nxt + offset) -- Absolute index for the next item.
process_error(format(msg, specifier, starting_from, extra or ""), unpack(specs))
end
local function check_disallow_holes(params, val, name, listname, extra)
for i = 1, val.maxindex do
if val[i] == nil then
hole_error(params, name, listname, i, num_keys(val)[i], extra)
end
end
end
local function handle_holes(params, val, name)
local param = params[name]
local disallow_holes = param.disallow_holes
-- Iterate up the list, and throw an error if a hole is found.
if disallow_holes then
check_disallow_holes(params, val, name, param.list, " or empty")
end
-- Iterate up the list, and throw an error if a hole is found due to a
-- missing parameter, treating empty parameters as part of the list. This
-- applies beyond maxindex if blank arguments are supplied beyond it, so
-- isn't mutually exclusive with `disallow_holes`.
local empty = val.empty
if param.disallow_missing then
if empty then
-- Remove `empty` from `val`, so it doesn't get returned.
val.empty = nil
for i = 1, max(val.maxindex, empty.maxindex) do
if val[i] == nil and not empty[i] then
local keys = extend(num_keys(val), num_keys(empty))
sort(keys)
hole_error(params, name, param.list, i, keys[i])
end
end
-- If there's no table of empty parameters, the check is identical to
-- `disallow_holes`, except that the error message only refers to
-- missing parameters, not missing or empty ones. If `disallow_holes` is
-- also set, there's no point checking again.
elseif not disallow_holes then
check_disallow_holes(params, val, name, param.list)
end
end
-- If `allow_holes` is set, there's nothing left to do.
if param.allow_holes then
-- do nothing
-- Otherwise, remove any holes: `pairs` won't work, as it's unsorted, and
-- iterating from 1 to `maxindex` times out with inputs like |100000000000=,
-- so use num_keys to get a list of numerical keys sorted from lowest to
-- highest, then iterate up the list, moving each value in `val` to the
-- lowest unused positive integer key. This also avoids the need to create a
-- new table. If `disallow_holes` is specified, then there can't be any
-- holes in the list, so there's no reason to check again; this doesn't
-- apply to `disallow_missing`, however.
else
if not disallow_holes then
local keys, i = num_keys(val), 0
while true do
i = i + 1
local key = keys[i]
if key == nil then
break
elseif i ~= key then
track("holes compressed")
val[i], val[key] = val[key], nil
end
end
end
-- Some code depends on only numeric params being present when no holes are
-- allowed (e.g. by checking for the presence of arguments using next()), so
-- remove `maxindex`.
val.maxindex = nil
end
end
local function maybe_flatten(params, val, name)
local param = params[name]
if param.flatten then
if param.allow_holes then
process_error("For parameter %s, can't set both `allow_holes` and `flatten`", name)
end
if not param.sublist and param.type ~= "genders" and param.type ~= "labels" and
param.type ~= "references" and param.type ~= "qualifier" then
process_error("For parameter %s, can only set `flatten` along with `sublist` or a list-generating type", name)
end
-- Do the flattening ourselves rather than calling flatten() in [[Module:table]], which will attempt to
-- flatten non-list objects like title objects, and cause an error in the process.
-- FIXME: We should do this in-place if possible.
local newlist = {}
for _, sublist in ipairs(val) do
for _, item in ipairs(sublist) do
insert(newlist, item)
end
end
val = newlist
end
return val
end
-- If both `template_default` and `default` are given, `template_default` takes precedence, but only on the template or
-- module page. This means a different default can be specified for the template or module page example. However,
-- `template_default` doesn't apply if any args are set, which helps (somewhat) with examples on documentation pages
-- transcluded into the template page. HACK: We still run into problems on documentation pages transcluded into the
-- template page when pagename= is set. Check this on the assumption that pagename= is fairly standard.
local function convert_default_val(name, param, pagename_set, any_args_set, add_empty_sublist)
if not pagename_set then
local val = param.template_default
if val ~= nil and not any_args_set and is_own_page() then
return convert_val(val, name, param, "template default")
end
end
local val = param.default
if val ~= nil then
return convert_val(val, name, param, "default")
-- Sublist parameters should return an empty table if not given, but only do
-- this if the parameter isn't also a list (in which case it will already
-- be an empty table).
-- FIXME: do this once all modules that pass in a sublist parameter treat an empty sublist identically to a nil argument; some currently do things based on the fact an argument exists at all.
-- elseif add_empty_sublist and param.sublist then
--return {}
end
end
--[==[
Process arguments with a given list of parameters. Return a table containing the processed arguments. The `args`
parameter specifies the arguments to be processed; they are the arguments you might retrieve from
{frame:getParent().args} (the template arguments) or in some cases {frame.args} (the invocation arguments). The `params`
parameter specifies a list of valid parameters, and consists of a table. If an argument is encountered that is not in
the parameter table, an error is thrown.
The structure of the `params` table is as described above in the intro comment.
'''WARNING:''' The `params` table is destructively modified to save memory. Nonetheless, different keys can share the
same value objects in memory without causing problems.
The `return_unknown` parameter, if set to {true}, prevents the function from triggering an error when it comes across an
argument with a name that it doesn't recognise. Instead, the return value is a pair of values: the first is the
processed arguments as usual, while the second contains all the unrecognised arguments that were left unprocessed. This
allows you to do multi-stage processing, where the entire set of arguments that a template should accept is not known at
once. For example, an inflection-table might do some generic processing on some arguments, but then defer processing of
the remainder to the function that handles a specific inflectional type.
]==]
function export.process(args, params, return_unknown)
-- Process parameters for specific properties
local args_new, args_unknown, any_args_set, required, patterns, list_args, index_list, args_placeholders, placeholders_n = {}
-- TODO: memoize the processing of each unique `param` value, since it's common for the same value to be used for many parameter names.
for name, param in pairs(params) do
validate_name(name, "parameter names")
if param ~= true then
local spec_type = type(param)
if type(param) ~= "table" then
internal_process_error(
"spec for parameter %s must be a table of specs or the value true, but found %s.",
name, spec_type ~= "boolean" and spec_type or param
)
end
-- Populate required table, and make sure aliases aren't set to required.
if param.required then
if required == nil then
required = {}
end
required[name] = true
end
local listname, alias_of = param.list, param.alias_of
if alias_of then
validate_name(alias_of, "the alias_of field of parameter ", name)
if alias_of == name then
internal_process_error(
"parameter %s cannot be an alias of itself.",
name
)
end
local main_param = params[alias_of]
-- Check that the alias_of is set to a valid parameter.
if not (main_param == true or type(main_param) == "table") then
internal_process_error(
"parameter %s is an alias of an invalid parameter.",
name
)
end
validate_alias_options(param, name, main_param, alias_of)
-- Aliases can't be lists unless the canonical parameter is also a list.
if listname and (main_param == true or not main_param.list) then
internal_process_error(
"list parameter %s is set as an alias of %s, which is not a list parameter.", name, alias_of
)
-- Can't be an alias of an alias.
elseif main_param ~= true then
local main_alias_of = main_param.alias_of
if main_alias_of ~= nil then
internal_process_error(
"alias_of cannot be set to another alias: parameter %s is set as an alias of %s, which is in turn an alias of %s. Set alias_of for %s to %s.",
name, alias_of, main_alias_of, name, main_alias_of
)
end
end
end
local replaced_by = param.replaced_by
if replaced_by then -- replaced_by can be `false`, which is OK
validate_name(replaced_by, "the replaced_by field of parameter ", name)
if replaced_by == name then
internal_process_error(
"parameter %s cannot be replaced by itself.",
name
)
end
local main_param = params[replaced_by]
-- Check that the replaced_by is set to a valid parameter.
if not (main_param == true or type(main_param) == "table") then
internal_process_error(
"parameter %s is set to be replaced by an invalid parameter.",
name
)
end
-- Can't be a replaced-by of a replaced-by.
if main_param ~= true then
local main_replaced_by = main_param.replaced_by
if main_replaced_by ~= nil then
internal_process_error(
"replaced_by cannot be set to another replaced-by parameter: parameter %s is set as replaced by %s, which is in turn replaced by %s. Set replaced_by for %s to %s.",
name, replaced_by, main_replaced_by, name, main_replaced_by
)
end
end
if param.instead ~= nil then
internal_process_error("the `instead` tag can only be given when `replaced_by` is set to `false`.")
end
elseif replaced_by == false then
if param.instead ~= nil and type(param.instead) ~= "string" then
internal_process_error(
"the `instead` tag must be a string, but saw %s.",
param.instead
)
end
end
if replaced_by ~= nil then
if param.reason ~= nil and type(param.reason) ~= "string" then
internal_process_error(
"the `reason` tag must be a string, but saw %s.",
param.reason
)
end
end
if listname then
if not alias_of then
local key = name
if type(name) == "string" then
key = gsub(name, "\1", "")
end
local list_arg = {maxindex = 0}
args_new[key] = list_arg
if list_args == nil then
list_args = {}
end
list_args[key] = list_arg
end
local list_type = type(listname)
if list_type == "string" then
-- If the list property is a string, then it represents the name
-- to be used as the prefix for list items. This is for use with lists
-- where the first item is a numbered parameter and the
-- subsequent ones are named, such as 1, pl2, pl3.
patterns = save_pattern(name, listname, patterns or {})
elseif listname ~= true then
internal_process_error(
"list field for parameter %s must be a boolean, string or undefined, but saw a %s.",
name, list_type
)
elseif type(name) == "number" then
if index_list ~= nil then
internal_process_error(
"only one numeric parameter can be a list, unless the list property is a string."
)
end
-- If the name is a number, then all indexed parameters from
-- this number onwards go in the list.
index_list = name
else
patterns = save_pattern(name, name, patterns or {})
end
if find(name, "\1", nil, true) then
if args_placeholders then
placeholders_n = placeholders_n + 1
args_placeholders[placeholders_n] = name
else
args_placeholders, placeholders_n = {name}, 1
end
end
end
end
end
--Process required changes to `params`.
if args_placeholders then
for i = 1, placeholders_n do
local name = args_placeholders[i]
params[gsub(name, "\1", "")], params[name] = params[name], nil
end
end
-- Process the arguments
for name, val in pairs(args) do
any_args_set = true
validate_name(name, "argument names", nil, true)
-- Guaranteeing that all values are strings avoids issues with type coercion being inconsistent between functions.
local val_type = type(val)
if val_type ~= "string" then
internal_process_error(
"argument %s has the type %s; all arguments must be strings.",
name, val_type
)
end
local orig_name, raw_type, index, canonical = name, type(name)
if raw_type == "number" then
if index_list and name >= index_list then
index = name - index_list + 1
name = index_list
end
elseif patterns then
-- Does this argument name match a pattern?
for pattern, pname in next, patterns do
index = match(name, pattern)
-- It matches, so store the parameter name and the
-- numeric index extracted from the argument name.
if index then
index = tonumber(index)
name = pname
break
end
end
end
local param = params[name]
-- If the argument is not in the list of parameters, store it in a separate list.
if not param then
args_unknown = unknown_param(name, val, args_unknown or {})
elseif param == true then
canonical = orig_name
val = php_trim(val)
if val ~= "" then
-- If the parameter is duplicated, throw an error.
if args_new[name] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
canonical
)
end
args_new[name] = val
end
else
if param.replaced_by == false then
process_error(
("Parameter %%s has been removed and is no longer valid%s.%s"):format(
param.reason and ", " .. param.reason or "",
param.instead and " Instead, " .. param.instead .. "." or ""),
name
)
elseif param.replaced_by then
process_error(
("Parameter %%s has been replaced by %%s%s."):format(
param.reason and ", " .. param.reason or ""),
name, param.replaced_by
)
end
if param.deprecated then
track("deprecated parameter", name)
end
if param.require_index then
-- Disallow require_index for numeric parameter names, as this doesn't make sense.
if raw_type == "number" then
internal_process_error(
"cannot set require_index for numeric parameter %s.",
name
)
-- If a parameter without the trailing index was found, and
-- require_index is set on the param, treat it
-- as if it isn't recognized.
elseif not index then
args_unknown = unknown_param(name, val, args_unknown or {})
end
end
-- Check that separate_no_index is not being used with a numeric parameter.
if param.separate_no_index then
if raw_type == "number" then
internal_process_error(
"cannot set separate_no_index for numeric parameter %s.",
name
)
elseif type(param.alias_of) == "number" then
internal_process_error(
"cannot set separate_no_index for parameter %s, as it is an alias of numeric parameter %s.",
name, param.alias_of
)
end
end
-- If no index was found, use 1 as the default index.
-- This makes list parameters like g, g2, g3 put g at index 1.
-- If `separate_no_index` is set, then use 0 as the default instead.
if not index and param.list then
index = param.separate_no_index and 0 or 1
end
-- Normalize to the canonical parameter name. If it's a list, but the alias is not, then determine the index.
local raw_name = param.alias_of
if raw_name then
raw_type = type(raw_name)
if raw_type == "number" then
name = raw_name
local main_param = params[raw_name]
if main_param ~= true and main_param.list then
if not index then
index = param.separate_no_index and 0 or 1
end
canonical = raw_name + index - 1
else
canonical = raw_name
end
else
name = gsub(raw_name, "\1", "")
local main_param = params[name]
if not index and main_param ~= true and main_param.list then
index = param.separate_no_index and 0 or 1
end
if not index or index == 0 then
canonical = name
elseif name == raw_name then
canonical = name .. index
else
canonical = gsub(raw_name, "\1", index)
end
end
else
canonical = orig_name
end
-- Only recognize demo parameters if this is the current template or module's
-- page, or its documentation page.
if param.demo and not is_own_page("include_documentation") then
args_unknown = unknown_param(name, val, args_unknown or {})
end
-- Remove leading and trailing whitespace unless no_trim is true.
if param.no_trim then
check_string_param_modifier(param.type, name, "no_trim")
else
val = php_trim(val)
end
-- Empty string is equivalent to nil unless allow_empty is true.
if param.allow_empty then
check_string_param_modifier(param.type, name, "allow_empty")
elseif val == "" then
-- If `disallow_missing` is set, keep track of empty parameters
-- via the `empty` field in `arg`, which will be used by the
-- `disallow_missing` check. This will be deleted before
-- returning.
if index and param.disallow_missing then
local arg = args_new[name]
local empty = arg.empty
if empty == nil then
empty = {maxindex = 0}
arg.empty = empty
end
empty[index] = true
if index > empty.maxindex then
empty.maxindex = index
end
end
val = nil
end
-- Allow boolean false.
if val ~= nil then
-- Convert to proper type if necessary.
local main_param = params[raw_name]
if main_param ~= true then
val = convert_val(val, orig_name, main_param or param)
end
-- Mark it as no longer required, as it is present.
if required then
required[name] = nil
end
-- Store the argument value.
if index then
local arg = args_new[name]
-- If the parameter is duplicated, throw an error.
if arg[index] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a list parameter has been entered without an index and with index 1 at the same time, or because a parameter alias has been used.",
canonical
)
end
arg[index] = val
-- Store the highest index we find.
local maxindex = arg.maxindex
if index > maxindex then
maxindex = index
end
if arg[0] ~= nil then
arg.default, arg[0] = arg[0], nil
if maxindex < 1 then
maxindex = 1
end
end
arg.maxindex = maxindex
if not params[name].list then
args_new[name] = val
-- Don't store index 0, as it's a proxy for the default.
elseif index > 0 then
arg[index] = val
end
else
-- If the parameter is duplicated, throw an error.
if args_new[name] ~= nil then
process_error(
"Parameter %s has been entered more than once. This is probably because a parameter alias has been used.",
canonical
)
end
if not raw_name then
args_new[name] = val
else
local main_param = params[raw_name]
if main_param ~= true and main_param.list then
local main_arg = args_new[raw_name]
main_arg[1] = val
-- Store the highest index we find.
if main_arg.maxindex < 1 then
main_arg.maxindex = 1
end
else
args_new[raw_name] = val
end
end
end
end
end
end
-- Remove holes in any list parameters if needed. This must be handled
-- straight after the previous loop, as any instances of `empty` need to be
-- converted to nil.
if list_args then
for name, val in next, list_args do
handle_holes(params, val, name)
end
end
-- If the current page is the template which invoked this Lua instance, then ignore the `require` flag, as it
-- means we're viewing the template directly. Required parameters sometimes have a `template_default` key set,
-- which gets used in such cases as a demo.
-- Note: this won't work on other pages in the Template: namespace (including the /documentation subpage),
-- or if the #invoke: is on a page in another namespace.
local pagename_set = args_new.pagename
-- Handle defaults.
for name, param in pairs(params) do
if param ~= true then
local arg_new = args_new[name]
if arg_new == nil then
args_new[name] = convert_default_val(name, param, pagename_set, any_args_set, true)
elseif param.list and arg_new[1] == nil then
local default_val = convert_default_val(name, param, pagename_set, any_args_set)
if default_val ~= nil then
arg_new[1] = default_val
if arg_new.maxindex == 0 then
arg_new.maxindex = 1
end
end
end
end
end
-- Flatten nested lists if called for. This must come after setting the default.
if list_args then
for name, val in next, list_args do
args_new[name] = maybe_flatten(params, val, name)
end
end
-- The required table should now be empty.
-- If any parameters remain, throw an error, unless we're on the current template or module's page.
if required and next(required) ~= nil and not is_own_page() then
params_list_error(required, "required")
-- Return the arguments table.
-- If there are any unknown parameters, throw an error, unless return_unknown is set, in which case return args_unknown as a second return value.
elseif return_unknown then
return args_new, args_unknown or {}
elseif args_unknown and next(args_unknown) ~= nil then
params_list_error(args_unknown, "not used by this template")
end
return args_new
end
return export
nvds2x1q4or3uw7hq15nht1c9ben6p9
Modul:Scribunto
828
2551
10386
2026-09-16T08:26:32Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local math_module = "Module:math" local dump = mw.dumpObject local format = string.format local gsub = string.gsub local match = string.match local php_trim -- defined below local sub = string.sub local tonumber = tonumber local tostring = tostring local type = type do local php_htmlspecialchars_data local function get_php_htmlspecialchars_data() php_htmlspecialchars_data, get_php_htmlspecialchars_data = { ["\""] = """, ["&"] = "&..."
10386
Scribunto
text/plain
local export = {}
local math_module = "Module:math"
local dump = mw.dumpObject
local format = string.format
local gsub = string.gsub
local match = string.match
local php_trim -- defined below
local sub = string.sub
local tonumber = tonumber
local tostring = tostring
local type = type
do
local php_htmlspecialchars_data
local function get_php_htmlspecialchars_data()
php_htmlspecialchars_data, get_php_htmlspecialchars_data = {
["\""] = """,
["&"] = "&",
["'"] = "'",
["<"] = "<",
[">"] = ">",
}, nil
return php_htmlspecialchars_data
end
--[==[Lua equivalent of PHP's {{code|php|htmlspecialchars($string)}}, which converts the characters `&"'<>` to HTML entities.
If the `quotes` flag is set to {"compat"}, then `'` will not be converted, and if it is set to {"noquotes"}, then neither `"` nor `'` will be converted.]==]
function export.php_htmlspecialchars(str, quotes)
if quotes == nil or quotes == "quotes" then
quotes = "'\""
elseif quotes == "compat" then
quotes = "\""
elseif quotes == "noquotes" then
quotes = ""
else
local quotes_type = type(quotes)
error('`quotes` must be "quotes", "compat", "noquotes" or nil; received ' ..
(quotes_type == "string" and dump(quotes) or "a " .. quotes_type))
end
return (gsub(str, "[&<>" .. quotes .. "]", php_htmlspecialchars_data or get_php_htmlspecialchars_data()))
end
end
do
local function tonumber_extended(...)
tonumber_extended = require(math_module).tonumber_extended
return tonumber_extended(...)
end
-- Normalizes a string for use in comparisons which emulate PHP's equals
-- operator, which coerces certain strings to numbers: those within the
-- range -2^63 to 2^63 - 1 which don't have decimal points or exponents are
-- coerced to integers, while any others are coerced to doubles if possible;
-- otherwise, they remain as strings. PHP and Lua have the same precision
-- for doubles, but Lua's integer precision range is -2^53 + 1 to 2^53 - 1.
-- Any integers within Lua's precision, as well as all doubles, are simply
-- coerced to numbers, but PHP integers outside of Lua's precision are
-- emulated as normalized strings, with leading 0s and any + sign removed.
-- The `not_long` flag is used for the second comparator if the first did
-- not get normalized to a long integer, as PHP will only coerce strings to
-- integers if it's possible for both comparators.
local function php_normalize_string(str, not_long)
local num = tonumber_extended(str, nil, true)
-- Must be a number that isn't ±infinity, NaN or hexadecimal.
if not num or match(str, "^%s*[+-]?0[xX]()") then
return str
-- If `not_long` is set or `num` is within Lua's precision, return as a
-- number.
elseif not_long or num < 9007199254740992 and num > -9007199254740992 then
return num, "number"
end
-- Check if it could be a long integer, and return as a double if not.
local sign, str_no_0 = match(str, "^%s*([+-]?)0*(%d+)$")
if not str_no_0 then
return num, "number"
end
-- Otherwise, check if it's a long integer. 2^63 is 9223372036854775808,
-- so slice off the last 15 digits and deal with the two parts
-- separately. If the integer value would be too high/low, return as a
-- string.
local high = tonumber(sub(str_no_0, 1, -16))
if high > 9223 then
return str
elseif high == 9223 then
local low = tonumber(sub(str_no_0, -15))
-- Range is -2^63 to 2^63 - 1 (not symmetrical).
if low > 372036854775808 or low == 372036854775808 and sign ~= "-" then
return str
end
end
return (sign == "+" and "" or sign) .. str_no_0, "long integer", num
end
--[==[Lua equivalent of PHP's {{code|php|===}} operator for strings.]==]
function export.php_string_equals(str1, str2)
if str1 == str2 then
return true
end
local str1, str1_type, str1_num = php_normalize_string(str1)
if str1 == str2 then
return true
elseif str1_type == "long integer" then
local str2, str2_type = php_normalize_string(str2)
return str2 == (str2_type == "number" and str1_num or str1)
elseif str1_type == "number" then
return str1 == php_normalize_string(str2, true)
end
return false
end
end
--[==[Lua equivalent of PHP's {{code|php|trim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "}. This is useful when dealing with template parameters, since the native parser trims them like this.]==]
function export.php_trim(str)
return match(str, "[^ \t-\v\r%z].*%f[ \t-\v\r%z]") or ""
end
php_trim = export.php_trim
--[==[Lua equivalent of PHP's {{code|php|ltrim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "} from the beginning of the input string.]==]
function export.php_ltrim(str)
return (gsub(str, "^[ \t-\v\r%z]+", ""))
end
--[==[Lua equivalent of PHP's {{code|php|rtrim($string)}}, which trims {"\0"}, {"\t"}, {"\n"}, {"\v"}, {"\r"} and {" "} from the end of the input string.]==]
function export.php_rtrim(str)
return match(str, "^.+%f[ \t-\v\r%z]") or ""
end
--[==[Takes a template or module parameter name as either a string or number, and returns the Scribunto-normalized form (i.e. the key that that parameter would have in a {frame.args} table). For example, {"1"} (a string) is normalized to {1} (a number), {" foo "} is normalized to {"foo"}, and {1.5} (a number) is normalized to {"1.5"} (a string). Inputs which cannot be normalized (e.g. booleans) return {nil}.
Strings are trimmed with {export.php_trim}, unless the `no_trim` flag is set. If it is, then string parameters are not trimmed, but strings may still be converted to numbers if they do not contain whitespace; this is necessary when normalizing keys into the form received by PHP during callbacks, before any trimming occurs (e.g. in the table of arguments when calling {frame:expandTemplates()}).
After trimming (if applicable), keys are then converted to numbers if '''all''' of the following are true:
# They are integers; i.e. no decimals or leading zeroes (e.g. {"2"}, but not {"2.0"} or {"02"}).
# They are ≤ 2{{sup|53}} and ≥ -2{{sup|53}}.
# There is no leading sign unless < 0 (e.g. {"2"} or {"-2"}, but not {"+2"} or {"-0"}).
# They contain no leading or trailing whitespace (which may be present when the `no_trim` flag is set).
Numbers are converted to strings if '''either''':
# They are not integers (e.g. {1.5}).
# They are > 2{{sup|53}} or < -2{{sup|53}}.
When converted to strings, integers ≤ 2{{sup|63}} and ≥ -2{{sup|63}} are formatted as integers (i.e. all digits are given), which is the range of PHP's integer precision, though the actual output may be imprecise since Lua's integer precision is > 2{{sup|53}} to < -2{{sup|53}}. All other numbers use the standard formatting output by {tostring()}.]==]
function export.scribunto_parameter_key(key, no_trim)
local key_type = type(key)
if key_type == "string" then
if not no_trim then
key = php_trim(key)
end
if match(key, "^()-?[1-9]%d*$") then
local num = tonumber(key)
-- Lua integers are only precise to 2^53 - 1, so specifically check
-- for 2^53 and -2^53 as strings, since a numerical comparison won't
-- work as it can't distinguish 2^53 from 2^53 + 1.
return (
num <= 9007199254740991 and num >= -9007199254740991 or
key == "9007199254740992" or
key == "-9007199254740992"
) and num or key
end
return key == "0" and 0 or key
elseif key_type == "number" then
-- No special handling needed for inf or NaN.
return key % 1 == 0 and (
key <= 9007199254740992 and key >= -9007199254740992 and key or
key <= 9223372036854775808 and key >= -9223372036854775808 and format("%d", key)
) or tostring(key)
end
return nil
end
--[==[Takes a template or module parameter value as either a string, number or boolean, and returns the Scribunto-normalized form (i.e. the value that that parameter would have in a {frame.args} table), which is always a string. For example, {"foo"} remains the same, {2} (a number) is normalized to {"2"} (a string), {true} is normalized to {"1"}, and {false} is normalized to {""}. Inputs which cannot be normalized (e.g. tables) return {nil}.
By default, returned values are not trimmed, which matches the treatment of unnamed parameters (e.g. `bar` in {{tl|<nowiki/>foo|bar}}). If the `named` flag is set, then returned values will be trimmed, which matches the treatment of named parameters (e.g. `baz` in {{tl|<nowiki/>foo|bar=baz}}).]==]
function export.scribunto_parameter_value(value, named)
local value_type = type(value)
if value_type == "string" then
return named and php_trim(value) or value
elseif value_type == "number" then
return tostring(value)
elseif value_type == "boolean" then
return value and "1" or ""
end
return nil
end
return export
90dvo9iqmpdordtuzghbnnl9rw4476r
Modul:Fun
828
2552
10388
2026-09-16T08:29:09Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local debug_track_module = "Module:debug/track" local chain -- Defined below. local chain_iter -- Defined below. local getmetatable = getmetatable local gmatch = string.gmatch local ipairs = ipairs local pairs = pairs local pcall = pcall local rawequal = rawequal local rawget = rawget local select = select local setmetatable = setmetatable local tostring = tostring local type = type local unpack = unpack --[==[ Loaders for functions in other mod..."
10388
Scribunto
text/plain
local export = {}
local debug_track_module = "Module:debug/track"
local chain -- Defined below.
local chain_iter -- Defined below.
local getmetatable = getmetatable
local gmatch = string.gmatch
local ipairs = ipairs
local pairs = pairs
local pcall = pcall
local rawequal = rawequal
local rawget = rawget
local select = select
local setmetatable = setmetatable
local tostring = tostring
local type = type
local unpack = unpack
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function debug_track(...)
debug_track = require(debug_track_module)
return debug_track(...)
end
local function _iterString(iter, i)
i = i + 1
local char = iter()
if char ~= nil then
return i, char
end
end
-- Iterate over UTF-8-encoded codepoints in string.
local function iterString(str)
return _iterString, gmatch(str, ".[\128-\191]*"), 0
end
--[==[
Return {true} if the input is a function or functor (a table which can be called like a function, because it has a {__call} metamethod).
Note: if the input is a table with a protected metatable (i.e. one hidden using the `__metatable` metamethod), then this function will treat the value of `__metatable` as though it is the metatable, as that is what gets returned by `getmetatable` in such cases. If you are making use of the `__metatable` metamethod, make sure that `__metatable` is a table with a function at the `__call` key to ensure that this function returns the correct result; it does not matter if this function is the true `__call` metamethod.]==]
function export.is_callable(f)
local f_type = type(f)
if f_type == "function" then
return true
elseif f_type ~= "table" then
return false
end
-- A table is a functor if it has a `__call` metamethod. The only way to truly confirm this is by trying to call the table, but that could modify the table or other variables out of scope, so look for a `__call` metamethod instead. If the metatable is protected with `__metatable`, this may not be possible.
local mt = getmetatable(f)
if mt == nil then
return false
end
-- Check if the metatable is protected: `setmetatable` will throw an error if so.
local success = pcall(setmetatable, f, mt)
-- If it's protected, then `mt` could be anything, but use the heuristic that if a `__call` key exists then that's probably intentional.
-- This also builds in ways to ensure that this function always returns the correct result when implementing protected metatables.
if not success then
if type(mt) ~= "table" then
debug_track("fun/is_callable/protected metatable")
return false
end
local __metatable = rawget(mt, "__metatable")
-- If the value of `__metatable` is also `mt`, then `mt` must be the true metatable anyway (e.g. mw.loadData does this).
if __metatable == nil or not rawequal(mt, __metatable) then
debug_track("fun/is_callable/protected metatable")
end
end
local __call = rawget(mt, "__call")
-- `__call` metamethods have to be functions, so don't recurse to check it.
return __call ~= nil and type(__call) == "function"
end
--[==[
Takes two or more functions as arguments, and returns a new function which calls each of the input functions in turn. Any arguments given to the returned function are given to the first function, and all other functions receive the output value(s) from the previous function.]==]
function export.chain(func1, func2, ...)
local function chained_func(...)
return func2(func1(...))
end
if select("#", ...) == 0 then
return chained_func
end
return chain(chained_func, ...)
end
chain = export.chain
--[==[
Takes a generator function (i.e. a function that returns an iterator, such as {ipairs}) and one or more additional functions, and returns a new generator function. Any arguments given to the new generator (e.g. an input table) are given to the original generator, and the additional functions are called on each iteration. The first additional function takes the output from the original iterator (i.e. the function returned by the original generator), and any further functions receive the output value(s) from the previous function. This can be used to modify the values returned from an iterator.]==]
function export.chainIter(gen, new_iter, ...)
if select("#", ...) > 0 then
new_iter = chain(new_iter, ...)
end
return function(...)
local orig_iter, state, k = gen(...)
-- k has to be the first value returned by orig_iter on the last iteration, not whatever new_iter returned.
local function catch_values(...)
k = ...
if k ~= nil then
return new_iter(...)
end
end
return function()
return catch_values(orig_iter(state, k))
end, state, k
end
end
chain_iter = export.chainIter
do
local function catch_values(start, iter, state, k, ...)
if start == k or k == nil then
return k, ...
end
return catch_values(start, iter, state, iter(state, k))
end
function export.iterateFrom(start, iter, state, k)
local first = true
return function(state, k)
if first then
first = false
return catch_values(start, iter, state, iter(state, k))
end
return iter(state, k)
end, state, k
end
end
-- map(function(number) return number ^ 2 end,
-- { 1, 2, 3 }) --> { 1, 4, 9 }
-- map(function (char) return string.char(string.byte(char) - 0x20) end,
-- "abc") --> { "A", "B", "C" }
function export.map(func, iterable, isArray)
local array = {}
for k, v in (type(iterable) == "string" and iterString or (isArray or iterable[1] ~= nil) and ipairs or pairs)(iterable) do
array[k] = func(v, k, iterable)
end
return array
end
function export.mapIter(func, iter, state, init)
-- init could be anything
local array, i = {}, 0
for x, y in iter, state, init do
i = i + 1
array[i] = func(y, x, state)
end
return array
end
do
local function iter_tuples(tuples)
local i = tuples.i
if i > 1 then
i = i - 1
tuples.i = i
return unpack(tuples[i])
end
end
-- Takes an iterator function, and returns a new iterator that iterates in reverse, given the same arguments.
-- Note: changes to the state during iteration are not taken into account, since all the return values are calculated in advance.
function export.reverseIter(func)
return function(...)
-- Store all returned values as a list of tuples, then iterate in reverse over that list.
local tuples, i, iter, state, val1 = {}, 0, func(...)
while true do
i = i + 1
local vals = {iter(state, val1)}
-- Terminates if the first return value is nil, even if other values are non-nil.
val1 = vals[1]
if val1 == nil then
tuples.i = i
return iter_tuples, tuples
end
tuples[i] = vals
end
end
end
end
function export.forEach(func, iterable, isArray)
for k, v in (type(iterable) == "string" and iterString or (isArray or iterable[1] ~= nil) and ipairs or pairs)(iterable) do
func(v, k, iterable)
end
return nil
end
-------------------------------------------------
-- From http://lua-users.org/wiki/CurriedLua
-- reverse(...) : take some tuple and return a tuple of elements in reverse order
--
-- e.g. "reverse(1,2,3)" returns 3,2,1
local function reverse(...)
-- reverse args by building a function to do it, similar to the unpack() example
local function reverseHelper(acc, v, ...)
if select("#", ...) == 0 then
return v, acc()
else
return reverseHelper(function() return v, acc() end, ...)
end
end
-- initial acc is the end of the list
return reverseHelper(function() return end, ...)
end
function export.curry(func, numArgs)
-- currying 2-argument functions seems to be the most popular application
numArgs = numArgs or 2
-- no sense currying for 1 arg or less
if numArgs <= 1 then return func end
-- helper takes an argTrace function, and number of arguments remaining to be applied
local function curryHelper(argTrace, n)
if n == 0 then
-- kick off argTrace, reverse argument list, and call the original function
return func(reverse(argTrace()))
else
-- "push" argument (by building a wrapper function) and decrement n
return function(onearg)
return curryHelper(function() return onearg, argTrace() end, n - 1)
end
end
end
-- push the terminal case of argTrace into the function first
return curryHelper(function() return end, numArgs)
end
-------------------------------------------------
-- some(function(val) return val % 2 == 0 end,
-- { 2, 3, 5, 7, 11 }) --> true
function export.some(func, t, isArray)
for k, v in ((isArray or t[1] ~= nil) and ipairs or pairs)(t) do
if func(v, k, t) then
return true
end
end
return false
end
-- all(function(val) return val % 2 == 0 end,
-- { 2, 4, 8, 10, 12 }) --> true
function export.all(func, t, isArray)
for k, v in ((isArray or t[1] ~= nil) and ipairs or pairs)(t) do
if not func(v, k, t) then
return false
end
end
return true
end
function export.filter(func, t, isArray)
local new_t = {}
if isArray or t[1] ~= nil then -- array
local new_i = 0
for i, v in ipairs(t) do
if func(v, i, t) then
new_i = new_i + 1
new_t[new_i] = v
end
end
else
for k, v in pairs(t) do
if func(v, k, t) then
new_t[k] = v -- or create array?
end
end
end
return new_t
end
function export.fold(func, t, accum)
for i, v in ipairs(t) do
accum = func(accum, v, i, t)
end
return accum
end
-------------------------------
-- Fancy stuff
local function capture(...)
local vals = {n = select("#", ...), ...}
return function()
return unpack(vals, 1, vals.n)
end
end
-- Log input and output of function.
-- Receives a function and returns a modified form of that function.
function export.logReturnValues(func, prefix)
return function(...)
local inputValues = capture(...)
local returnValues = capture(func(...))
if prefix then
mw.log(prefix, inputValues())
mw.log(returnValues())
else
mw.log(inputValues())
mw.log(returnValues())
end
return returnValues()
end
end
export.log = export.logReturnValues
-- Convenience function to make all functions in a table log their input and output.
function export.logAll(t)
for k, v in pairs(t) do
if type(v) == "function" then
t[k] = export.logReturnValues(v, tostring(k))
end
end
return t
end
return export
nkdbp9odnwolm26t6b7nw37k4e1o1de
Modul:Table
828
2553
10389
2026-09-16T08:30:22Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} --[==[ intro: This module provides functions for dealing with Lua tables. All of them, except for two helper functions, take a table as their first argument. Some functions are available as methods in the arrays created by [[Module:array]]. Functions by what they do: * Create a new table: ** `shallowCopy`, `deepCopy`, `removeDuplicates`, `numKeys`, `compressSparseArray`, `keysToList`, `reverse`, `invert`, `listToSet` * Create an array: ** `remov..."
10389
Scribunto
text/plain
local export = {}
--[==[ intro:
This module provides functions for dealing with Lua tables. All of them, except for two helper functions, take a table
as their first argument.
Some functions are available as methods in the arrays created by [[Module:array]].
Functions by what they do:
* Create a new table:
** `shallowCopy`, `deepCopy`, `removeDuplicates`, `numKeys`, `compressSparseArray`, `keysToList`, `reverse`, `invert`, `listToSet`
* Create an array:
** `removeDuplicates`, `numKeys`, `compressSparseArray`, `keysToList`, `reverse`
* Return information about the table:
** `size`, `length`, `contains`, `isArray`, `deepEquals`
* Treat the table as an array (that is, operate on the values in the array portion of the table: values indexed by
consecutive integers starting at {1}):
** `removeDuplicates`, `length`, `contains`, `serialCommaJoin`, `reverseIpairs`, `reverse`, `invert`, `listToSet`, `isArray`
* Treat a table as a sparse array (that is, operate on values indexed by non-consecutive integers):
** `numKeys`, `maxIndex`, `compressSparseArray`, `sparseConcat`, `sparseIpairs`
* Generate an iterator:
** `sparseIpairs`, `sortedPairs`, `reverseIpairs`
* Other functions:
** `sparseConcat`, `serialCommaJoin`, `reverseConcat`
The original version was a copy of {{w|Module:TableTools}} on Wikipedia via [[c:Module:TableTools|Module:TableTools]] on
Commons, but in the course of time this module has been almost completely rewritten, with many new functions added. The
main legacy of this is the use of camelCase for function names rather than snake_case, as is normal in the English
Wiktionary.
]==]
local load_module = "Module:load"
local math_module = "Module:math"
local table = table
local concat = table.concat
local dump = mw.dumpObject
local ipairs = ipairs
local ipairs_default_iter = ipairs{export}
local next = next
local pairs = pairs
local require = require
local select = select
local signed_index -- defined as export.signedIndex
local table_len -- defined as export.length
local type = type
--[==[
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]==]
local function is_integer(...)
is_integer = require(math_module).is_integer
return is_integer(...)
end
local function safe_require(...)
safe_require = require(load_module).safe_require
return safe_require(...)
end
--[==[
Given an array and a signed index, returns the true table index. If the signed index is negative, the array will be counted from the end, where {-1} is the highest index in the array; otherwise, the returned index will be the same. To aid optimization, the first argument may be a number representing the array length instead of the array itself; this is useful when the array length is already known, as it avoids recalculating it each time this function is called.]==]
function export.signedIndex(t, k)
if not is_integer(k) then
error("index must be an integer")
end
return k < 0 and (type(t) == "table" and table_len(t) or t) + k + 1 or k
end
signed_index = export.signedIndex
--[==[
An iterator which works like `pairs`, but ignores any `__pairs` metamethod.]==]
function export.rawPairs(t)
return next, t, nil
end
--[==[
An iterator which works like `ipairs`, but ignores any `__ipairs` metamethod.]==]
function export.rawIpairs(t)
return ipairs_default_iter, t, 0
end
--[==[
This returns the length of a table, or the first integer key n counting from 1 such that t[n + 1] is nil. It is a more reliable form of the operator `#`, which can become unpredictable under certain circumstances due to the implementation of tables under the hood in Lua, and therefore should not be used when dealing with arbitrary tables. `#` also does not use metamethods, so will return the wrong value in cases where it is desirable to take these into account (e.g. data loaded via `mw.loadData`). If `raw` is set, then metamethods will be ignored, giving the true table length.
For arrays, this function is faster than `export.size`.]==]
function export.length(t, raw)
local n = 0
if raw then
for i in ipairs_default_iter, t, 0 do
n = i
end
return n
end
repeat
n = n + 1
until t[n] == nil
return n - 1
end
table_len = export.length
local function getIteratorValues(i, j , step, t_len)
i, j = i and signed_index(t_len, i), j and signed_index(t_len, j)
if step == nil then
i, j = i or 1, j or t_len
return i, j, j < i and -1 or 1
elseif step == 0 or not is_integer(step) then
error("step must be a non-zero integer")
elseif step < 0 then
return i or t_len, j or 1, step
end
return i or 1, j or t_len, step
end
--[==[
Given an array `list` and function `func`, iterate through the array applying {func(r, k, v)}, and returning the result,
where `r` is the value calculated so far, `k` is an index, and `v` is the value at index `k`. For example,
{reduce(array, function(a, _, v) return a + v end)} will return the sum of `array`.
Optional arguments:
* `i`: start index; negative values count from the end of the array
* `j`: end index; negative values count from the end of the array
* `step`: step increment
These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or
backwards and by how much, based on these inputs (see examples below for default behaviours).
Examples:
# No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default).
# step=-1 results in backward iteration from the end to the start in steps of 1.
# i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1).
# j=-3 results in forward iteration from the start to the 3rd last index.
# j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==]
function export.reduce(t, func, i, j, step)
i, j, step = getIteratorValues(i, j, step, table_len(t))
local ret = t[i]
for k = i + step, j, step do
ret = func(ret, k, t[k])
end
return ret
end
do
local function replace(t, func, i, j, step, generate)
local t_len = table_len(t)
-- Normalized i, j and step, based on the inputs.
local norm_i, norm_j, norm_step = getIteratorValues(i, j, step, t_len)
if norm_step > 0 then
i, j, step = 1, t_len, 1
else
i, j, step = t_len, 1, -1
end
-- "Signed" variables are multiplied by -1 if `step` is negative.
local t_new, signed_i, signed_j = generate and {} or t, norm_i * step, norm_j * step
for k = i, j, step do
-- Replace the values iff they're within the i to j range and `step` wouldn't skip the key.
-- Note: i > j if `step` is positive; i < j if `step` is negative. Otherwise, the range is empty.
local signed_k = k * step
if signed_k >= signed_i and signed_k <= signed_j and (k - norm_i) % norm_step == 0 then
t_new[k] = func(k, t[k])
-- Otherwise, add the existing value if `generate` is set.
elseif generate then
t_new[k] = t[k]
end
end
return t_new
end
--[==[
Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and
`v` is the value at index `k`), replacing the relevant values with the result. For example,
{apply(array, function(_, v) return 2 * v end)} will double each member of the array.
Optional arguments:
* `i`: start index; negative values count from the end of the array
* `j`: end index; negative values count from the end of the array
* `step`: step increment
These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or
backwards and by how much, based on these inputs (see examples below for default behaviours).
Examples:
# No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default).
# step=-1 results in backward iteration from the end to the start in steps of 1.
# i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1).
# j=-3 results in forward iteration from the start to the 3rd last index.
# j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==]
function export.apply(t, func, i, j, step)
return replace(t, func, i, j, step, false)
end
--[==[
Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and
`v` is the value at index `k`), and return a shallow copy of the original array with the relevant values replaced. For example,
{generate(array, function(_, v) return 2 * v end)} will return a new array in which each value has been doubled.
Optional arguments:
* `i`: start index; negative values count from the end of the array
* `j`: end index; negative values count from the end of the array
* `step`: step increment
These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or
backwards and by how much, based on these inputs (see examples below for default behaviours).
Examples:
# No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default).
# step=-1 results in backward iteration from the end to the start in steps of 1.
# i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1).
# j=-3 results in forward iteration from the start to the 3rd last index.
# j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==]
function export.generate(t, func, i, j, step)
return replace(t, func, i, j, step, true)
end
end
--[==[
Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and
`v` is the value at index `k`), and returning whether the function is true for all iterations.
Optional arguments:
* `i`: start index; negative values count from the end of the array
* `j`: end index; negative values count from the end of the array
* `step`: step increment
These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or
backwards and by how much, based on these inputs (see examples below for default behaviours).
Examples:
# No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default).
# step=-1 results in backward iteration from the end to the start in steps of 1.
# i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1).
# j=-3 results in forward iteration from the start to the 3rd last index.
# j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==]
function export.all(t, func, i, j, step)
i, j, step = getIteratorValues(i, j, step, table_len(t))
for k = i, j, step do
if not func(k, t[k]) then
return false
end
end
return true
end
--[==[
Given an array `list` and function `func`, iterate through the array applying {func(k, v)} (where `k` is an index, and
`v` is the value at index `k`), and returning whether the function is true for at least one iteration.
Optional arguments:
* `i`: start index; negative values count from the end of the array
* `j`: end index; negative values count from the end of the array
* `step`: step increment
These must be non-zero integers. The function will determine where to iterate from, whether to iterate forwards or
backwards and by how much, based on these inputs (see examples below for default behaviours).
Examples:
# No values for i, j or step results in forward iteration from the start to the end in steps of 1 (the default).
# step=-1 results in backward iteration from the end to the start in steps of 1.
# i=7, j=3 results in backward iteration from indices 7 to 3 in steps of 1 (i.e. step=-1).
# j=-3 results in forward iteration from the start to the 3rd last index.
# j=-3, step=-1 results in backward iteration from the end to the 3rd last index.]==]
function export.any(t, func, i, j, step)
i, j, step = getIteratorValues(i, j, step, table_len(t))
for k = i, j, step do
if not not (func(k, t[k])) then
return true
end
end
return false
end
--[==[
Joins an array with serial comma and serial conjunction, normally {"and"}. An improvement on {mw.text.listToText},
which doesn't properly handle serial commas.
Options:
* `conj`: Conjunction to use; defaults to {"and"}.
* `punc`: Punctuation to use; default to {","}.
* `dontTag`: Don't tag the serial comma and serial {"and"}. For error messages, in which HTML cannot be used.
* `dump`: Each item will be serialized with {mw.dumpObject}. For warnings and error messages.]==]
function export.serialCommaJoin(seq, options)
-- If the `dump` option is set, determine the table length as part of the
-- dump loop, instead of calling `table_len` separately.
local length
if options and options.dump then
local i, item = 1, seq[1]
if item ~= nil then
local dumped = {}
repeat
dumped[i] = dump(item)
i = i + 1
item = seq[i]
until item == nil
seq = dumped
end
length = i - 1
else
length = table_len(seq)
end
if length == 0 then
return ""
elseif length == 1 then
return seq[1]
end
local conj = options and options.conj
if conj == nil then
conj = "and"
end
if length == 2 then
return seq[1] .. " " .. conj .. " " .. seq[2]
end
local punc, dont_tag
if options then
punc = options.punc
if punc == nil then
punc = ","
end
dont_tag = options.dontTag
else
punc = ","
end
local comma
if dont_tag then
comma = "" -- since by default the serial comma doesn't display, when we can't tag we shouldn't display it.
conj = " " .. conj .. " "
else
comma = "<span class=\"serial-comma\">" .. punc .. "</span>"
conj = "<span class=\"serial-and\"> " .. conj .. "</span> "
end
return concat(seq, punc .. " ", 1, length - 1) .. comma .. conj .. seq[length]
end
--[==[
A function which works like `table.concat`, but respects any `__index` metamethod. This is useful for data loaded via `mw.loadData`.]==]
function export.concat(t, sep, i, j)
local list, k = {}, 0
while true do
k = k + 1
local v = t[k]
if v == nil then
return concat(list, sep, i, j)
end
list[k] = v
end
end
--[==[
Add a list of aliases for a given key to a table. The aliases must be given as a table.]==]
function export.alias(t, k, aliases)
for _, alias in pairs(aliases) do
t[alias] = t[k]
end
end
local mt = {}
function mt:__index(k)
local submodule = safe_require("Module:table/" .. k)
self[k] = submodule
return submodule
end
return setmetatable(export, mt)
exw27st65r96fvgc4krew9cy4eyvdow
Modul:Load
828
2554
10390
2026-09-16T08:31:50Z
Zhilal Darma
23
←Mambuek laman baisi "local export = {} local load_data = mw.loadData local loaded = package.loaded local main_loader = package.loaders[2] local require = require local setmetatable = setmetatable local loaders, loaded_data, mt local function get_mt() mt, get_mt = {__mode = "kv"}, nil return mt end -- main_loader returns a loader function if the module exists, or nil if it doesn't. local function get_loader(modname) if loaders == nil then local loader = main_loader(modname) lo..."
10390
Scribunto
text/plain
local export = {}
local load_data = mw.loadData
local loaded = package.loaded
local main_loader = package.loaders[2]
local require = require
local setmetatable = setmetatable
local loaders, loaded_data, mt
local function get_mt()
mt, get_mt = {__mode = "kv"}, nil
return mt
end
-- main_loader returns a loader function if the module exists, or nil if it doesn't.
local function get_loader(modname)
if loaders == nil then
local loader = main_loader(modname)
loaders = setmetatable({[modname] = loader or false}, mt or get_mt())
return loader
end
local loader = loaders[modname]
if loader == nil then
loader = main_loader(modname)
loaders[modname] = loader or false
end
return loader or nil
end
local function get_data(modname, loader)
if loaded_data == nil then
local data = load_data(modname)
loaded_data = setmetatable({[loader] = data}, mt or get_mt())
return data
end
local data = loaded_data[loader]
if data == nil then
data = load_data(modname)
loaded_data[loader] = data
end
return data
end
--[==[
Like `require`, but returns `nil` if a module does not exist, instead of throwing an error.
Outputs are cached, which is faster for all modules, but much faster for nonexistent modules, since `require` will attempt to use the full loader each time because loading failures don't get cached.]==]
function export.safe_require(modname)
local mod = loaded[modname]
if mod == nil and get_loader(modname) then
-- Call with require instead of the loader directly, as it protects against infinite loading loops.
return require(modname)
end
return mod
end
--[==[
Like `mw.loadData`, but it does not generate a new table each time the same module is loaded via this function. Instead, it caches the returned data so that the same table can be returned each time, which is more efficient.]==]
function export.load_data(modname)
return get_data(modname, get_loader(modname))
end
--[==[
Like `export.load_data` (itself a variant of `mw.loadData`), but returns `nil` if a module does not exist, instead of throwing an error.
Outputs are cached, which is faster for all modules, but much faster for nonexistent modules, since `mw.loadData` will attempt to use the full loader each time because loading failures don't get cached.]==]
function export.safe_load_data(modname)
local loader = get_loader(modname)
if loader == nil then
return nil
end
return get_data(modname, loader)
end
return export
ane0ji5isx9pytu6xaiacn5a70oo2t2
Pangguno:Riolin Putri
2
2555
10392
2026-09-16T08:42:17Z
Riolin Putri
80
←Mambuek laman baisi "Halo sanak sadonyo, Ambo Putri. {{Wikilatih|acara=Festival Literasi Budaya|di=Kota Pariaman|tgl=26 September 2026}}"
10392
wikitext
text/x-wiki
Halo sanak sadonyo, Ambo Putri.
{{Wikilatih|acara=Festival Literasi Budaya|di=Kota Pariaman|tgl=26 September 2026}}
eyjnlevx2qt7kp20jx8jnzvge8z8pif